文章目录
-
- [default constructor](#default constructor)
- [copy constructor](#copy constructor)
- 参考
default constructor
当编译器需要构造函数的时候,而类本身没有定义构造函数时,会被合成出来,被合成的构造函数只会执行编译器所需的动作。
带有default constructor的成员对象
如果一个类没有任何构造函数,但它内含一个成员对象,而这个成员对象有默认构造函数,那么这个类的隐式默认构造函数就是"nontrival"的,编译器需要为该class合成出一个默认构造函数。不过这个合成操作只会在constructor真正需要被调用时才会发生。
如果一个类A 内含一个或一个以上的成员类对象,那么类A的每一个构造函数必须调用每个成员类的默认构造函数。编译器会扩张已存在的constructors,在其中安插一些代码,使得用户代码被执行前,先调用必要的默认构造函数。
如果是多个成员类对象,C++要求以成员在类中声明顺序来调用各个构造函数。这一点由编译器完成,它为每一个constructor安插程序代码,以成员声明顺序调用每一个成员所关联的默认构造函数。这些代码将安插在用户代码前面。
带有default constructor的基类
如果一个没有任何构造函数的类派生自一个"带有默认构造函数"的基类,那么这个派生类的默认构造函数会被是为nontrival,并因此需要被合成出来。它将调用上一层基类的默认构造函数(根据它们的声明顺序)。对一个后继派生的类而言,这个合成的构造函数和一个"被显式提供的默认构造函数"没有什么差异。
如果已经提供了多个构造函数,但其中都没有默认构造函数。编译期会扩张现有的每一个构造函数,将"用以调用所有必要之默认构造函数"的程序代码加进去。他不会合成一个新的默认构造函数,因为其他"由用户所提供的构造函数"存在的缘故。如果同时也存在着"带有默认构造函数"的成员类对象,那些默认构造函数也会被调用------在所有基类构造函数都被调用之后。
带有一个虚函数的类
另有两种情况,也需要合成出默认构造函数:
- 类声明(或继承)一个虚函数
- 类派生自一个继承串链,其中有一个或更多的虚基类(virutal base class)
不管哪一种情况,由于缺乏由用户声明的构造函数,编译期会详细记录合成一个默认构造函数的必要信息
下面两个扩张行为会在编译期间发生:
- 一个virtual function table会被编译期产生出来,内放类的virtual functions地址
- 在每一个类对象中,一个额外的pointer member(即vptr)会被编译期合成出来,内含相关的类vtbl的地址
带有一个虚基类的类
必须使vritual base class在其每一个derived class object中的位置,能够于执行期准备妥当。
总结
有4种情况,会造成编译期必须为未声明constructor的类合成一个default constructor.
C++标准把那些合成物称为implicit nontrivial default constructors.
被合成出来的constructor只能满足编译期(非程序)的需要。它之所以能够完成任务,是借着"调用member object或base class的default constructor"或是"为每一个object初始化其virtua fuction机制或virtual base class机制"而完成的。至于没有存在那4种情况而又没有声明任何constructor的class,我们说它们拥有的是implicit trivial default constructors,它们实际上并不会被合成出来。
在合成的defaullt constructor中,只有base class subobjects和member class objects会被初始化。所有其他的nonstatic data member(如整数、整数指针、整数数组等等)都不会被初始化。这些初始化操作对程序而言或许有必要,但对编译期而言则非必要。如果程序需要一个"把某指针设为0"的default constructor,那么提供它的人应该是程序员。
copy constructor
如果一个类没有提供一个explicit copy constructor, 此时以另外一个对象赋值时,其内部是将每个成员对象的值从一个对象拷贝到另一个对象。这种可以自动进行的拷贝过程称为默认拷贝构造。
一个对象可以用两种方式复制得到:
- 被初始化,即被拷贝构造
- 被指定(assignment),即拷贝赋值
C++标准把copy constructor分为trivial和nontrivial的。只有nontrivial的实例才会被合成于程序中。决定一个copy constructor是否为trivial的标准在于class是否展现出所谓的"bitwise copy semantics(位逐次拷贝)"。
下面四种情况下,一个类不会展现出"bitwise copy semantics":
- 当类内含一个成员对象而该对象的类声明有一个copy constructor时(不论是被类设计者显式声明,或是被编译器合成)
- 当类继承自一个基类,而后者存在一个copy constructor时(不论是被显式声明或是被合成的)
- 当class声明一个或多个virtual functions时
- 当class派生自一个继承串链,其中有一个或多个virtual base classes时
前两种情况下,编译期必须将member或者base class的"copy constructors调用操作"安排到被合成的copy constructor中。
在上面四种情况下,类不再保持"bitwise copy semantics",而且default copy constructor如果未被声明的话,会被视为nontrivial。这种情况下,如果缺乏一个已声明的copy constructor,编译器为了正确处理"以一个class object作为另一个class object的初值",必须合成出一个copy constructor。
参考
C++杂记------返回值优化(RVO,Return Value Optimization)、具名返回值优化 (NRVO,Named Return Value Optimization)