[学习笔记] C++ 历年试题解析(三)--小补充
小小的补充一下吧,因为李老师又把直招的卷子发出来了。。
题目
1.有指针变量定义及初始化int *p=new int[10];执行delete [] p;操作将结束指针变量p的生命期。(×)
解释:试了一下,p还真能再使用,所以不要理解错了,delete释放的是p指向内存的空间,他自己还活着,不过指向有点问题,所以delete之后还是让p指向NULL吧,这也是我们经常应当做的。
2.RMB那题,老师上课讲的,详细解析下
#include <iostream> using namespace std; class RMB { public: RMB(unsigned int x=0) { yuan = x / 100; jiao = x / 10 % 10; fen = x % 10; } operator unsigned int() const { // 类型转换函数,可将RMB类型的对象转换成unsigned int类型数据 return 100*yuan+10*jiao+fen; } RMB & operator++() { return *this = *this + 1; } RMB operator++(int) { RMB temp(*this); ++(*this); return temp; } friend ostream & operator<<(ostream & out, const RMB & r) { out <<"¥"<< r.yuan <<"元"<< r.jiao <<"角"<< r.fen <<"分"; return out; } protected: unsigned int yuan, jiao, fen; }; int main() { RMB rmb(12345); cout << rmb << endl; rmb = 100; cout << rmb << endl; cout << ++(++rmb) << endl; cout << rmb++ << endl; cout << rmb << endl; rmb = 100; cout << 2*rmb << endl; rmb = 2*rmb; cout << rmb << endl; rmb = 100; cout << rmb/2 << endl; cout << RMB(rmb/2) << endl; cout << rmb << endl; rmb = 11; rmb = rmb*rmb; cout << rmb << endl; cout << ((rmb > 80)? "" : "不") << "大于80分" << endl; return 0; } /* ¥123元4角5分 ¥1元0角0分 ¥1元0角2分 ¥1元0角2分 ¥1元0角3分 200 ¥2元0角0分 50 ¥0元5角0分 ¥1元0角0分 ¥1元2角1分 大于80分 */
解析:
至上而下分析(从main函数),用12345初始化RMB类型对象rmb,然后输出rmb,显然第一行输出“¥123元4角5分”,往下走rmb=100,这个操作是赋值操作,我们先往上看看有没有重载赋值运算符函数,发现好像并没有哦,这时候系统会找一找int类里面有没有类型转换构造函数把int转为RMB类型(显然没有嘛,因为RMB是我们自定义的数据类型),而到了这时候呢,系统为了完成类型转换的工作,会直接调用我们的构造函数,将100作为参数传到构造函数里,“相当于”完成了一个类型转换的工作。好了,现在100已经是个RMB对象了,两边对象类型相同,赋值。我们说了,由于没有重载赋值运算符,所以系统会默认将拷贝构造函数作为赋值运算符函数,往上一看,拷贝构造函数也没定义,所以这个调用的是系统隐式定义的浅拷贝构造函数,完成赋值操作。往下走,输出rmb对象,“ ¥1元0角0分”,调用前增量运算符函数,上面重载了,是引用返回,所以两次增量之后还是rmb对象,那么应该按照该对象的输出函数进行输出,也就是“ ¥1元0角2分 ”,下一步调用后增量运算符函数,上面也重载过了,是值返回,返回的是RMB类型的无名变量,对其输出,按照RMB类型的输出函数输出,“ ¥1元0角2分”。后面输出rmb对象,自然是“¥1元0角3分”,下一步又让rmb=100,这里rmb还是RMB类型的,输出2*rmb,由于int类里面没有定义类型转换函数把int转为RMB类型,而RMB类里面定义了把RMB类转为int类型的函数,所以系统会直接调用这个转换函数,把rmb转换为int类型,所以结果应该是int型的,输出的就是int,“200”,然后下一句rmb=2*rmb,刚才解释过,2*rmb得到的是个整数200,其实这句话就相当于rmb=200,前面又说过这句话相当于直接调用RMB的构造函数,那这就很明显了嘛。输出rmb对象,按照RMB类型输出函数进行输出,“ ¥2元0角0分 ”。下一句rmb=100不用解释了,输出rmb/2一样的,把rmb转换为整数,然后输出int“ 50”,下一句相当于输出RMB(50),按照RMB类型输出函数进行输出,“ ¥0元5角0分”,然后输出rmb,rmb并没变嘛,“ ¥1元0角0分 ”,rmb=11,让后是rmb = rmb*rmb,先看看有没有重载*运算符函数,没有,而*在int中有定义,刚好RMB类定义了强制转换为int的函数,那么系统就帮我们调用类型转换函数将两者都转为int,相乘,得到int 121,然后将121赋值给RMB类型rmb对象,然后输出“ ¥1元2角1分”。最后比较,也是转换为int比较,输出“ 大于80分”。
3.冒号语法补充一点
冒号语法的出现是为了解决在构造函数执行时要用到成员类时没有初始化的问题,使用冒号语法即在构造函数后面加上冒号,写上成员类的初始化,这与直接在构造函数里赋值是不一样的,在构造函数之前完成的是初始化而不是赋值。要注意的是,其后成员初始化顺序与其书写顺序是无关的。这句话的解释是,与你声明的顺序有关,举个例子吧。
class A{ public: A():c(5.6),b(6){ xxx } private: int b; double c; };
这段代码并不先初始化c,而是先初始化b,因为下面先声明的b。
好的,那么还有个问题,看下面的代码:
class A{ public: A(int n=0){ a = n; } private: int a; }; class B{ public: B(int n=0){ b = n; } private: int b; }; class C{ public: A():aa(5){ //do nothing } private: A aa; B bb; }
C类我们只给aa初始化了值,那么bb呢,其实也初始化了,按照默认参数0初始化的,可以自己实验输出得到结果的。那这样就告诉我们,在C类对象析构的时候,由于先创建的对象应该后析构,所以后析构的是aa,最后才是本类的对象。因为冒号语法就是保证在C类构造函数执行之前进行其他成员的初始化的任务的,因此就算不用冒号语法,系统其实也会隐式地帮我们写A和B类对象的初始化。所以这样析构的顺序就懂了。