作为一个C工程师,或多或少都读过不少C语言的书籍,或精品或残次,我记了很多笔记,这里整理出来自己对构造函数的理解。
默认构造函数
默认构造函数既可以是用户自定义的,也可以是编译器合成出来的。编译器在需要的时候才会合成有意义的构造函数,其他时候合成构造函数不执行任何操作(C++对象模型里称此时实际上没有合成出来)。合成的默认构造函数是否有实际意义由是否是编译器需要而定。
1、非编译器需要
类的数据成员是内置类型或合成类型,此时合成的默认构造函数是没有意义的,a和b的值将是内存上次被使用后的遗迹。
1 | class A |
2、编译器需要
分为五种情况:类的数据成员是某个类的对象,且该类定义了默认构造函数;派生类的基类定义了默认构造函数;类声明或者继承了一个虚函数;类派生自一个含有虚基类的继承树;派生类虚继承于基类。此时合成的默认构造函数是有意义的。
情况一和二:
1 | class A{ |
情况三、四:
1 | class A{ |
情况五:
1 | class X { public: int i; } |
3、编译器需要和非编译器需要混合情况
1 | class A{ |
4、自定义默认构造函数
1 | class A{ |
5、未定义默认构造函数
如果已经定义了其他构造函数,编译器不会再合成默认构造函数,此时建议定义一个默认构造函数。
拷贝构造函数
拷贝构造函数有三个使用场景:
- 明确以一个对象的内容作为另外一个对象的初值
- 函数传参
- 函数返回值
如果class没有声明一个拷贝构造函数,就会合成拷贝构造函数,如果class只做bitwise copy操作,合成的拷贝构造函数是没有意义的(C++对象模型说其其实没有被合成);只有class不做bitwise copy操作,合成的拷贝构造函数才是有意的,真的被合成出来。有如下情况:
- 当class内含一个member object而后者的class声明有一个copy constructor
- 当class继承自一个base class而后者有一个copy constructor
- 当class声明一个或多个virtual function,继承体系中执行拷贝构造会重设virtual table指针
- 当class派生自一个继承串联,其中有一个或多个虚基类,
构造函数初始化列表
构造函数初始化列表在构造函数体之前执行,负责类数据成员的初始化。如果没有定义初始化列表,在构造函数体执行会存在效率问题——此时相当于先使用初始化列表默认初始化数据成员,然后在函数体内执行拷贝操作。并且以下情况必须要列表初始化:
- const或者reference成员必须要列表初始化
- 没有默认构造函数的成员对象必须要列表初始化
- 基类没有默认构造函数必须要列表初始化。
这里要注意一点,我们自己定义初始化列表时有可能不会在意其初始化顺序,实际上,类成员函数在初始化列表中初始化顺序是依照其在类定义中出现的顺序,基类数据成员的初始化早于派生类数据成员。这也就意味着,初始化列表中的数据成员的书写顺序和数据成员的真正初始化顺序无关,这是很重要的。看下面一段代码:
1 | class X{ |
编译器会对初始化列表一一处理,并可能重新排序,以反映出数据成员的声明次序,它会安插一些代码到构造函数体内,并置于任何构造函数内用户定义代码之前。
explicit修饰的构造函数
explicit关键词是为了防止单参数的构造函数被当做一个conversion运算符。