【注:解答全部为本人所写,仅供同学们学习时参考使用,请勿照搬抄袭!】
1、
1)略
2)如果main,f1,g1,g2或更多的函数之间有更为复杂的调用关系,头文件一般按怎样的规律写呢?
一般情况下,头文件应按照以下规律编写:
- 头文件名应与包含的函数或类名有关
- 在头文件中应包含必要的预处理指令,例如#ifndef、#define和#endif,以避免重复包含
- 应包含函数或类的声明,但不包括其定义
- 应包含其他必要的头文件,以确保在使用特定函数或类时所需的所有内容都可用
3)请总结出cpp文件与.h文件的关系,以及头文件中存放的内容。
C++中,.cpp文件包含函数或类的实现,而.h文件则包含函数或类的声明。头文件是一种重要的C++编程工具,可帮助程序员在不同的文件中共享代码。头文件中通常包含函数或类的声明、结构体的定义和宏定义。
4)若main,f1,g1,g2函数中都用到常量YEARDAYS(365),和WON(1),TUE(2),WED(3),THU(4),FRI(5),STA(6),SUN(7)
如何写头文件?
可以将常量定义放在单独的头文件中,并在需要使用这些常量的文件中包含该头文件。这将确保在整个程序中都可以使用这些常量,同时也更容易进行修改和维护。因此,以下是如何编写包含常量的头文件:
// constants.h
#ifndef CONSTANTS_H
#define CONSTANTS_H
const int YEARDAYS = 365;
const int WON = 1;
const int TUE = 2;
const int WED = 3;
const int THU = 4;
const int FRI = 5;
const int SAT = 6;
const int SUN = 7;
#endif
2.以max函数为例,实现不同参数传递方式(传值/传引用),不同参数常量性,
以及不同的返回值类型(值返回,引用返回,常量引用返回)等情况下,max函数的
参数虚实结合和max的各种使用和它们间的异同。注意参数不同形式时,哪些形式的调用是可以的?哪些算重载函数?哪些是完全等价的?
int max(int,int);
int max(const int, const int);
const int max(int,int);
int max(int&,int&);
int& max(int&,int&);
int& max(const int&,const int&);
const int& max(int&,int&);
const int& max(const int&,const int&);
…
Main函数中,可能有如下的调用:
int retValue01 = max(1,2);
int retValue02 = max(a,b);
max(a,3) = 5;
max(a,b)=max(c,d );
等形式。
max函数的参数虚实结合和使用方式可以有多种,包括传值、传引用、常量参数、返回值类型等。以下是各种情况下max函数的声明和定义:
以下是各种情况下max函数的声明和定义,以及可能的调用方式和异同点的总结:
- 传值和常量参数
int max(int a, int b); // 值返回
int max(const int a, const int b); // 常量值返回
const int max(int a, int b); // 常量值返回
这些函数接受两个int型参数,并返回较大的值。它们是根据传递的参数类型和返回类型的不同而进行重载的。
对于这些函数,可以用常量或变量来调用它们。对于常量值返回类型的函数,不能用左值调用函数。
- 传引用和常量引用参数
int max(int& a, int& b); // 引用返回
int& max(int& a, int& b); // 引用返回
int& max(const int& a, const int& b); // 常量引用返回
const int& max(int& a, int& b); // 常量引用返回
这些函数接受两个int型引用或常量引用参数,并返回较大的值。它们是根据传递的参数类型和返回类型的不同而进行重载的。
对于引用返回类型的函数,可以用左值调用函数。对于常量引用返回类型的函数,可以用常量或变量来调用它们。
有些函数可能是重载函数,有些函数可能是完全等价的。例如,传递int和const int参数的max函数是重载函数,而传递int和const int&参数的max函数是完全等价的。
3.练习类的定义与多文件实现
类A的定义在头文件a.h中,
class A {
public:
void Display() const {
cout<<“The argment is NULL”<<endl;
}
void Display(const char * str) const {
cout<<"The argment is "<<str<<endl;
}
void OtherFunc() {
}
};
main函数写在mainApp.cpp文件中,
int main(int argc,char * argv[])
{
A aA;
if (argc>1) {
aA.Display(argv[1]);
} else {
aA.Display();
}
cout<<sizeof(A)<<endl;
return 0;
}
a) 查看上述程序的执行结果。理解命令行参数、对象的大小、函数重载。
程序的执行结果取决于命令行参数的数量。如果argc大于1,则将argv[1]传递给A的Display函数,否则将调用Display函数的无参版本。
在最后,程序会打印出A对象的大小(以字节为单位)。函数重载是在同一作用域内定义多个具有相同名称但参数列表不同的函数。
b)现将Display函数分别改成以inline形式和纯外联实现,试一试。理解内联/外联实现在编程中的注意事项。
将Display函数实现为内联函数的方式是在函数定义之前添加关键字inline。
c) 试一试在a.h中,写或不写包含警戒,程序是否正确执行。
在a.h文件中应该写包含警戒,以确保头文件只被编译一次。如果没有包含警戒,可能会导致重复定义的错误。
d)给类A增加一个整型的类变量aStaticInt和一个类方法,在main函数中调用该方法。
掌握类变量的声明和定义。进一步,试一试,类变量是一个数组呢?
在类A中添加一个静态成员变量aStaticInt和一个静态成员函数,可以在类定义中声明该变量和函数,但需要在实现文件中定义它们。在main函数中,可以使用作用域解析运算符(::)访问静态成员变量和函数。
例如:
// a.h
class A {
public:
static int aStaticInt;
static void StaticFunc();
// 其他成员函数
};
// a.cpp
#include "a.h"
int A::aStaticInt = 0;
void A::StaticFunc() {
// 实现
}
// mainApp.cpp
#include "a.h"
int main(int argc,char * argv[])
{
A::aStaticInt = 10;
A::StaticFunc();
// 其他代码
return 0;
}
可以将aStaticInt定义为数组,例如:
static int aStaticInt[10];
e)现再增加一个类B,并放入b.h中
class B {
public:
void Func() {
cout<<"This is member function Func()"<<endl;
}
private:
A aA;
};
并在主函数中调用B中的成员函数Func.
在b.h中添加B类的定义,并在main函数中创建B对象并调用其成员函数:
// b.h
#include "a.h"
class B {
public:
void Func();
private:
A aA;
};
// mainApp.cpp
#include "b.h"
int main(int argc,char * argv[])
{
B b;
b.Func();
// 其他代码
return 0;
}
f) 针对e),重新按c)中的要求试一试。若将Func的实现改成外联实现,按c)中的要求,再试一试。
准确理解包含警戒的作用,以及各cpp文件独立编译的含义。
如果将Func函数的实现定义为外联函数,则需要将函数声明添加到b.h文件中,并在另一个文件中实现函数。由于Func函数中包含对A类的引用,因此需要包含a.h文件。此外,需要确保各个cpp文件都被独立编译,并在链接时进行链接。以下是相关代码示例:
// b.h
#ifndef B_H
#define B_H
#include "a.h"
class B {
public:
void Func();
private:
A aA;
};
#endif // B_H
// b.cpp
#include "b.h"
void B::Func() {
// 实现
}
// mainApp.cpp
#include "b.h"
int main(int argc,char * argv[])
{
B b;
b.Func();
// 其他代码
return 0;
}
g)将e)中的B改成:
class B {
public:
B(A& aA) { pA = &aA; }
void Func() {
cout<<“This is member function Func()”<<endl;
}
void NewFunc()
{
pA->OtherFunc();
}
private:
A * pA;
};
试一试,让程序编译通过并执行。
将B类的构造函数中的参数类型更改为指向A对象的指针,并在NewFunc函数中使用指针操作符(->)调用OtherFunc函数。
// b.h
#include "a.h"
class B {
public:
B(A* pa) : pA(pa) {}
void Func();
void NewFunc();
private:
A* pA;
};
// b.cpp
#include "b.h"
void B::Func() {
// 实现
}
void B::NewFunc() {
pA->OtherFunc();
}
// mainApp.cpp
#include "a.h"
#include "b.h"
int main(int argc,char * argv[])
{
A a;
B b(&a);
b.Func();
b.NewFunc();
// 其他代码
return 0;
}
h)将B中的NewFunc函数以外联的形式实现,b.h可以怎么写?
理解class A;与#include "a.h"的含义差别,理解外联实现的好处。
在b.h文件中添加对A类的前向声明(class A;),然后在b.cpp文件中包含a.h文件
class A;是A类的前向声明,用于告知编译器A类的存在,但不包含A类的定义。
这可以减少头文件的依赖性,提高编译效率。但是,前向声明只能用于声明指向A类对象的指针或引用,并不能使用A类的成员函数和数据成员。
#include "a.h"是包含A类的完整定义,可以在当前文件中使用A类的所有成员函数和数据成员。
外联实现的好处在于,可以将函数定义放在单独的cpp文件中,使得程序模块化和可维护性更好。外联函数的声明在头文件中,这样其他文件可以访问该函数,但是定义在单独的cpp文件中,可以避免重复定义错误。此外,外联函数的实现可以放在库文件中,可以在多个程序中共享使用,提高代码的复用性。
4.练习将实际生活中的事物,抽象成类,并给出类的设计
(找出数据成员和成员函数并用C++的类表示出来即可)
1)笔、钢笔
2)文件、目录
3)打印机、显示器
4)太阳、月亮
1)笔、钢笔
类名:Pen
数据成员:
- inkColor (string):笔的墨水颜色
成员函数:
- refill(int inkAmount):加墨水
2)文件、目录
类名:File
数据成员:
- fileName (string):文件名
- fileSize (int):文件大小
- fileExtension (string):文件扩展名
- filePath (string):文件路径
- createdDate (string):文件创建日期
成员函数:
- open():打开文件
- close():关闭文件
- rename(string newName):重命名文件
-delet(): 删除文件
类名:Directory
数据成员:
- dirName (string):目录名
- dirPath (string):目录路径
- subDirectories (vector<Directory>):子目录列表
- files (vector<File>):文件列表
成员函数:
- addSubDirectory(Directory subDir):添加子目录
- addFile(File file):添加文件
- removeSubDirectory(Directory subDir):移除子目录
- removeFile(File file):移除文件
3)打印机、显示器
类名:Printer
数据成员:
- isConnected (bool):打印机是否已连接
- inkLevel (int):墨水量
- paperLevel (int):纸张量
成员函数:
- print(string text):打印文本
- checkInkLevel():检查墨水量
- checkPaperLevel():检查纸张量
类名:Monitor
数据成员:
- resolution (string):分辨率
- isConnected (bool):显示器是否已连接
- brightness (int):亮度
- contrast (int):对比度
成员函数:
- displayImage(Image image):显示图像
- adjustBrightness(int value):调节亮度
- adjustContrast(int value):调节对比度
4)太阳、月亮
类名:Sun
数据成员:
- diameter (double):太阳直径
- temperature (int):太阳表面温度
- mass (double):太阳质量
成员函数:
- emitEnergy():辐射能量
- rotate():自转
类名:Moon
数据成员:
- diameter (double):月球直径
- distanceFromEarth (double):距离地球的距离
- mass (double):月球质量
成员函数:
- orbit():绕地球轨道运动
- reflectSunlight():反射阳光
- string getName(); // 获取名称
- float getRadius(); // 获取半径
- float getDistanceToEarth(); // 获取距离地球的距离
- bool isSun(); // 判断是否为太阳
5.练习读懂类的代码,练习使用已有的类。
下面的TRandom类是用线性调和算法,实现的一个伪随机数发生器,仔细阅读代码,理解其含义。
线性调和算法的基本原理是:对于给定的N、M和K,任给一个X,
不断用X=((N*X+M) mod K )进行迭代计算,那么得到的无穷多个x值,
近似于在(0,K)上的均匀分布.其中N,M,K为较大的数(N最好是素数).
#include <limits.h> //声明INT_MAX和ULONG_MAX常量
#include <windows.h> //声明GetTickCount()函数,其返回从开机到现在经过的毫秒数
//#include <stdlib.h> //声明rand和srand函数
//#include <time.h> //声明time函数
class TRandom
{
public:
//缺省使用系统时间(开机后经过的毫秒数)为种子
TRandom (long seed=0) { mSeed=(seed?seed: GetTickCount()); }
//也可以定义自己的种子
void Seed(long seed=0) { mSeed=(seed?seed: GetTickCount( )); }
//取得一个随机的整数
int Integer() { return Next();}
//取得一个在指定范围内的随机整数
int Integer(int min,int max) { return min+Next()%(max-min+1);}
//取得一个随机的(0到1之间的)小数
double Real() {return double(Next())/double(INT_MAX);}
private:
//使用调和算法
void Change() {mSeed=(314159265*mSeed+13579)%ULONG_MAX;}
//取得伪随机数发生器的随机数序列中的下一个随机整数
int Next() {
int loops=mSeed%3;
for (int i=0;i<=loops;i++)
Change ();
return int(mSeed/2);
}
unsigned long mSeed; //随机数发生器的种子
};
现有一个大小为54的整型数组,其元素分别为0,1,2,…,53.
1) 注意类中的public和private, 理解访问控制的作用.
public和private是C++中的访问控制关键字,用于限制类的成员的访问权限。
public成员可以被类外的代码访问和调用,而private成员只能被类内的成员函数访问和调用。在类的设计中,将数据成员设为private可以保证其不被直接访问和修改,而必须通过类的公有成员函数来完成相关的操作,增强了程序的安全性和可维护性。
2)请利用给出的TRandom类,完成此数组的”洗牌”过程,并输出洗牌后的顺序。写出相应的完整C++程序。
思考:用TRandom类,洗牌后,54张牌的排列大约多少种可能?现实中54张可有多少种排列方法?
#include <iostream>
#include "TRandom.h"
int main() {
int a[54];
for (int i = 0; i < 54; i++) {
a[i] = i;
}
TRandom r;
for (int i = 0; i < 54; i++) {
int j = r.Integer(i, 53); // 生成i到53之间的随机整数
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
for (int i = 0; i < 54; i++) {
std::cout << a[i] << " ";
}
std::cout << std::endl;
return 0;
}
3)利用给出的TRandom,使用投针法,估算PI值。
投针法:在半径为1的1/4圆内,随机投针,若落在圆内,则圆内计数加1;
这样,若总投掷数为N次,落在园内M次,随着N的增大,
M/N约等于PI/4,即PI约等于4*M/N.
#include <iostream>
#include <cmath>
#include "TRandom.h"
int main() {
TRandom r;
int total = 1000000; // 投针总次数
int inside = 0; // 落在圆内的次数
for (int i = 0; i < total; i++) {
double x = r.Real();
double y = r.Real();
if (x*x + y*y <= 1) {
inside++;
}
}
double pi = 4.0 * inside / total;
std::cout << "Estimated pi value: " << pi << std::endl;
std::cout << "Error: " << std::abs(pi - 3.141592653589793) << std::endl;
return 0;
}
>>通过投掷1000000次针,估算得到的π值约为3.141748,误差为0.000156。由于投针法的随机性,每次运行结果可能不同,但随着投掷次数的增加,估算结果逐渐趋近于π的真实值。
6.一个骰子(dice)有6个面,各面的点数分别为1,2,3,4,5,6,但各面之间的相对位置是固定的。
请实现Dice类,其主要完成
(1) 模拟掷骰子过程(Cast),并返回掷得的点数;
(2)报告当前各面的点数。
(3) 模拟一次掷两个或多个骰子,并返回得到的总点数。
实现该类时,取得随机数可使用系统函数srand和rand,也可使用上例中的TRandom类。文章来源:https://www.toymoban.com/news/detail-435133.html
注:本题算法不是很好,采用了6个if条件判断,原因是抛掷一次骰子所得到的点数所在面与观察者所面向的面的相对位置关系并不确定。也可以采用随机化三次,分别表示骰子在空中旋转时沿x,y,z轴方向旋转的次数的方法来解决。文章来源地址https://www.toymoban.com/news/detail-435133.html
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
class Dice {
public:
Dice() {
srand(time(NULL));
up = 1;
down = 6;
left = 2;
right = 5;
front = 3;
back = 4;
}
// 掷一次骰子(默认向上的点数为结果)
int cast() {
int idx = rand() % 6 + 1; // 生成1~6的随机数
up = idx;
down = 7 - up;
if (up == 3) {
while(1) {
temp = rand() % 6 + 1;
if (temp != up && temp != down) break;
}
front = temp;
back = 7 - front;
if (front == 6) {
left = 2;
right = 5;
}
if (front == 1) {
left = 5;
right = 2;
}
if (front == 2) {
left = 1;
right = 6;
}
if (front == 5) {
left = 6;
right = 1;
}
}
if (up == 4) {
while(1) {
temp = rand() % 6 + 1;
if (temp != up && temp != down) break;
}
front = temp;
back = 7 - front;
if (front == 1) {
left = 2;
right = 5;
}
if (front == 6) {
left = 5;
right = 2;
}
if (front == 2) {
left = 6;
right = 1;
}
if (front == 5) {
left = 1;
right = 6;
}
}
if (up == 1) {
while(1) {
temp = rand() % 6 + 1;
if (temp != up && temp != down) break;
}
front = temp;
back = 7 - front;
if (front == 3) {
left = 2;
right = 5;
}
if (front == 4) {
left = 5;
right = 2;
}
if (front == 2) {
left = 4;
right = 3;
}
if (front == 5) {
left = 3;
right = 4;
}
}
if (up == 6) {
while(1) {
temp = rand() % 6 + 1;
if (temp != up && temp != down) break;
}
front = temp;
back = 7 - front;
if (front == 3) {
left = 5;
right = 2;
}
if (front == 4) {
left = 2;
right = 5;
}
if (front == 2) {
left = 3;
right = 4;
}
if (front == 5) {
left = 4;
right = 3;
}
}
if (up == 2) {
while(1) {
temp = rand() % 6 + 1;
if (temp != up && temp != down) break;
}
front = temp;
back = 7 - front;
if (front == 1) {
left = 3;
right = 4;
}
if (front == 6) {
left = 4;
right = 3;
}
if (front == 3) {
left = 6;
right = 1;
}
if (front == 4) {
left = 1;
right = 6;
}
}
if (up == 5) {
while(1) {
temp = rand() % 6 + 1;
if (temp != up && temp != down) break;
}
front = temp;
back = 7 - front;
if (front == 6) {
left = 3;
right = 4;
}
if (front == 1) {
left = 4;
right = 3;
}
if (front == 3) {
left = 1;
right = 6;
}
if (front == 4) {
left = 6;
right = 1;
}
}
return up;
}
// 输出当前各面的点数
void report() {
cout << "Points: " << endl;
cout << "上面的点数为:" << up << endl;
cout << "下面的点数为:" << down << endl;
cout << "前面的点数为:" << front << endl;
cout << "后面的点数为:" << back << endl;
cout << "左面的点数为:" << left << endl;
cout << "右面的点数为:" << right << endl;
cout << endl;
}
// 掷n个骰子,返回总点数
int castMulti(int n) {
int totalPoints = 0;
for (int i = 0; i < n; i++) {
totalPoints += cast();
}
return totalPoints;
}
private:
// int m_points[6]; // 存储各面的点数
int up;
int down;
int front;
int back;
int left;
int right;
int temp;
};
// 测试代码
int main() {
Dice d;
d.report(); // 输出初始点数
int point = d.cast();
cout << "Cast: " << point << endl;
int totalPoints = d.castMulti(3);
cout << "Total points: " << totalPoints << endl;
return 0;
}
到了这里,关于2022级吉林大学面向对象第一次上机测试的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!