Default Constructor什么时候才会被编译器生成出来呢?

文章内容基于《深度探索C++对象模型》P39~47整理得到,掺杂个人理解,如有错误,敬请斧正!
还可结合我另一篇博客一起看:Copy Constructor什么时候才会被编译器生成出来呢?
注:文中“合成”一词一般指的是“编译器implicit声明nontrivial的default constructor”这个动作

default constructor指的是在声明class object时可以不用给定参数的constructor。
分为两大类:

一、explicit声明的default constructor

(1)无参数的constructor
(2)所有参数均有默认值的constructor

二、编译器implicit声明的default constructor

分为两类:
(1)trivial(无用的)
(2)nontrivial(有用的)

  在C++ Standard中有如此内容:“对于class X,如果没有任何user-declared constructor,那么会有一个default constructor被implicit声明出来……一个被implicit声明出来的constructor将是一个trivial constructor……”。前半句我们应该在别的书中见过类似的:“如果没有explicit声明任何constructor,则编译器会自动声明一个全为空(参数列表、函数体均为空)的constructor”。而后半句“一个被implicit声明出来的constructor将是一个trivial constructor”,那什么时候implicit声明出来的constructor是nontrivial的呢?

  通过对书本内容的理解,我觉得这句话应该拓展为如下内容:
  “对于class X,如果没有任何user-declared constructor,那么会有一个default constructor被implicit声明出来。若class X属于4种特殊情况时,这个constructor将会是nontrivial,这个constructor并非是空的,它有且只有编译器所需的行动;若class X不属于4种特殊情况,这个constructor将会是trivial,而且这个trivial implicit declared default constructor实际上并不会合成出来。此外,如果存在constructor(无论是编译器合成的,还是user-declared),且class X属于4种特殊情况时,编译器将会对class X所定义的每一个constructor进行扩张,在其中安插一些编译器需要的语句来完成编译器所需的行动。”

  那么,“4种特殊情况”和“编译器所需的行动”都指的是什么呢?

(1)“带有Default Constructor”的Member Class Object

  原文:“如果一个class没有任何constructor,但它内含一个member object,而后者有default constructor,那么这个class的implicit default constructor就是‘nontrivial’,编译器需要为该class合成出一个default constructor。不过这个合成操作只有在constructor真正需要被调用时才会发生。……编译器会扩张已存在的constructors,在其中安插一些代码,使得user code被执行之前,先调用必要的default constructors”

  换言之:class X有一个member class object是class Y的对象y1。若X没有任何constructor,但Y有default constructor。此时编译器为X声明的default constructor就不能全为空了,至少要调用Y的default constructor来构造y1。因此此时编译器为X合成的default constructor是nontrivial,因为它包含了对Y的default constructor的调用行动;若X有constructor(无论是编译器合成的,还是user-declared),则编译器会对每一个constructor进行扩张以调用Y的default constructor。

  举个例子:

编译器合成的constructor:
X::X(){
	y1.Y::Y(); //编译器安插的代码,对y1进行构造
}

对已有constructor的扩张:
X::X(){
	y1.Y::Y(); //编译器安插的代码,对y1进行构造
	……         //用户原本在X::X()中写的代码
}

如果有多个class member objects都要调用default constructor:
X::X(){
	//按多个class member objects声明顺序来调用
	y1.Y::Y(); //编译器安插的代码
	y2.Y::Y(); //编译器安插的代码
	z1.Z::Z(); //编译器安插的代码
	……         //用户原本在X::X()中写的代码
}

  注意:这里“编译器所需的行动”有且只有“对Y的default constructor的调用”。因此,编译器为X合成的default constructor中只包含了“对Y的default constructor的调用”这一种行动,而并不会产生任何代码对X的其他member进行初始化。因为y1的初始化时编译器的责任,对X其他member初始化时程序员的责任。

(2)“带有Default Constructor”的Base Class

  原文:“如果一个没有任何constructor的class派生自一个‘带有default constructor’的base class,那么这个derived class 的default constructor会被视为nontrivial,并因此需要被合成出来。它将调用上一层base classes的default constructor(根据它们的声明顺序)。……如果设计者提供多个constructors,但其中都没有default constructors,编译器会扩张现有的每一个constructors,将‘用以调用所有必要之default constructors’的程序代码加进去。……如果同时亦存在着‘带有default constructors’的member class objects,那些default constructor也会被调用——在所有base class constructor都被调用之后。”

  简而言之:此时合成的nontrivial default constructor是为了初始化base class

  有了(1)的内容,(2)应该比较好理解,不过是把member class objects换成了base class

(3)“带有一个Virtual Function”的Class

  原文:“编译器必须为每一个Widget(或其派生类的)object的vptr设定初值(注:这里Widget类有一个virtual function),放置适当的virtual table地址。对于class所定义的每一个constructor,编译器会安插一些代码来做这样的事情。对于那些未声明任何constructors的classes,编译器会为它们合成一个default constructor,以便正确初始化每一个class object的vptr。”

  简而言之:为了正确初始化vptr使其指向正确的virtual table。

(4)“带有一个Virtual Base Class”的Class

  这里其实涉及到virtual base class的subobject在其derived class object中是怎么安排的。根据《深度探索C++对象模型》P116开始的内容以及P121图3.5a可知,含有virtual base class的derived class object在内存中包含两部分:不变区域与共享区域。不变区域包含了非virtual base class的subobject,这些subobject在其后所有derived class object中的offset是固定的;共享区域包含了virtual base class的subobject,这些subobject在其后所有derived class object中的offset不是固定的,不同的derived class object中的offset有可能不同,因此需要额外的指针指向这些subobject来帮助确定随着派生发生变化的offset。

Default Constructor什么时候才会被编译器生成出来呢?_第1张图片

  原文:“必须使virtual base class在其每一个derived class object中的位置能够于执行期准备妥当。……在derived class object的每一个virtual base classes中安插一个指针来完成,所有‘经由reference或pointer来存取一个virtual base class’的操作都可以通过相关指针完成。……_vbcX(注:_vbcX指向virtual base class X在其derived class中的subobject)是在class object构造期间被完成的。对于class所定义的每一个constructor,编译器会安插那些‘允许每一个virtual base class的执行期存取操作’的代码。如果class没有声明任何constructors,编译器必须为它合成一个default constructor。”

  简而言之:和(3)类似,为了初始化derived class object中指向virtual base class subobject的指针

总结

  以上4种情况下由编译器合成的default constructor被称为implicit nontrivial default constructor,其只能满足编译器的需要,而不能满足程序的需要。这种constructor能“调用member class object和base class的default constructor”,也能“为vptr(指向虚函数表vtbl)和vbc(指向虚基类subobject)设定正确的值”,这是编译器的需要。而其他member并不会被初始化,因为这是程序的需要,是程序员的责任。
  而如果原本有constructor,不论是合成的,还是explicit声明的,编译器都会对其进行扩张,安插编译器所需的行动。
  对于不属于以上4种情况,且没有声明任何constructor的classes,编译器并不会生成所谓的implicit trivial default constructor。
  因此有两点需要知道:(1)没有定义default constructor时,编译器不一定会implicit声明一个出来;(2)编译器声明的default constructor并不会初始化每一个member;

你可能感兴趣的:(C++,c++)