类的构造函数学习笔记:显式调用构造函数,拷贝构造函数
在学习《C++primer 第五版》(中文版)中第七章类的时候遇到了一个有意思的习题,原题如下:
练习7.43:假定有一个名为NoDefault的类,它有一个接受int的构造函数,但是没有默认的构造函数。定义类C,C有一个Nodefault类型成员,定义C的默认构造函数
本题的答案很简单,直接利用初始化列表给成员类提供一个默认参数即可,参考答案如下:
struct Nodefault { private: int x; public: Nodefault(int y){}; }; struct C { private: Nodefault no; public: C() :no(1) {}; };
但是当时想利用一个为所有参数都提供默认实参的构造函数来等价定义默认函数,于是将C的构造函数改为下式:
C(Nodefault x= Nodefault(1)) :no(x) {};
刚入门的同学可能会这样理解:调用构造函数Nodefault(int)将其返回值当成x的默认初始值并利用列表初始化提供成员类no的初始值。这样理解有两个问题:
首先是构造函数是一个没有返回值的特殊函数,其次没有返回值的构造函数无法给"="提供一个有效的右值。
学过拷贝构造函数的同学可能这样理解:调用拷贝构造函数生成临时对象x,在通过拷贝构造函数初始化成员变量no。通过下文的测试显然这样理解也有偏差,
正确的执行流程应该是:
1、显式调用类Nodefault的构造函数Nodeafult(int),生成一个类的临时对象x
注意:下式只调用构造函数Nodefault(int),不会调用拷贝构造函数或者拷贝赋值运算符‘=”,并且为直接调用,expiicit关键字对此式无影响,此为显示调用构造函数的特性。学习了深入探索C++对象模型第二章后,我觉得也可以认为是拷贝构造函数被剔除(优化)了,因为Nodefault是bitwise的并且没有提供拷贝构造函数,所以此时编译器根本没有产生一个拷贝构造函数。
Nodefault x= Nodefault(1)
下式调用构造函数生成临时对象,然后调用赋值运算符“=”(使用delete删除operator=后,下式调用失败),注意两者区别。
x= Nodefault(1);
2、调用拷贝构造函数将临时对象x拷贝给类C的成员类no(只调用一次拷贝构造函数)。
验证代码如下:
struct Nodefault { private: int x; public: Nodefault(int y) { cout << "consttuction for Nodefault" << endl; }; Nodefault(const Nodefault &c) { x = c.x; cout << "copy construction" << endl; } ~Nodefault() { cout << "deconstruction for Nodefault" <<endl; } }; struct C { private: Nodefault no; public: C(Nodefault x= Nodefault(1)) :no(x){ cout << "consttuction for C" << endl; }; ~C() { cout << "deconstruction for C" << endl; } }; int main() { C object; return 0; }
输出结果如下:
1 consttuction for Nodefault 2 copy construction 3 consttuction for C 4 deconstruction for Nodefault 5 deconstruction for C 6 deconstruction for Nodefault
注意: 1、这个式子直接调用一次构造函数生成对象x
2、两次调用类Nodefault的析构函数:第一次在C对象构造函数结束时析构临时对象Nodefault、第二在程序结束时自动析构C后析构成员类Nodefaul