1、default constructor的构造操作
C++standard:对于class X,如果没有任何user-declared constructor,那么会有一个default constructor 被暗中(implicitly)声明出来,一个被暗中声明出来的default constructor将是一个trivial constructor。但是这个生成的defaultconstructor只能满足编译器的需要,而不是程序的需要,所有的nonstaticdata member都不会被初始化,要注意。
一个nontrivialdefault constructor在ARM的术语中就是编译器所需要的那种,必要的话会由编译器合成出来。下面讨论nontrivial defaultconstructor的四种情况。
(1)带有defaultconstructor的memberclass object:如果一个类没有任何constructor,但它内含一个memberobject,而后者有defaultconstructor,那么这个class的implicitdefault constructor就是”nontrivial“的。
既然编译器会合成默认构造函数,那么在C++各个不同的编译模块中,编译器如何避免合成出多个默认构造函数(比如一个是a.c文件,另一个是b.c文件)呢?解决办法是吧合成的default constructor、copyconstructor、destructor、assignmentcopy operator都以inline方式完成,一个inline函数有静态链接,不会被文件以外看到。如果函数太复杂,不适合做成inline,就会合成出一个explicitnon-inline static实体。
如果class内含一个以上的memberclass objects,那么此class的每一个constructor必须调用每一个memberclass的defaultconstructor。编译器会扩张已经存在的constructor,在其中安插一些代码,似的user code被执行之前,先调用必要的defaultconstructor,且以声明顺序调用。
(2)带有DefaultConstructor的BaseClass:如果一个没有任何constructor的class派生自一个带有defaultconstructor的base class,那么这个derivedclass的defaultconstructor会被视为nontrivial,并因此需要被合成出来。
(3)带有一个VirtualFunction的Class:两种情况
(a)class声明(或继承)一个virtual function
(b) class派生自一个继承串链,其中有一个或更多的virtual base classes
有两个扩张会在编译期间发生:一个virtualfunction table会被编译器产生出来,内放class的virtualfunctions地址;在每一个classobject中,一个额外的pointer member会被编译器合成出来,内含相关的class vtbl的地址。
(4)带有一个VIrtualBase Class的Class: VirtualBase Class的实现法在不同的编译器之间有极大差异,然而,每一种实现法必须使virtualbase class在每一个derivedclass object中的位置,能够于执行期准备妥当。
2、Copy Constructor的构建操作
(1)当classobject以相同class的另一个object作为初值时,其内部是以所谓的default memberwise(按成员)initialization手法完成的。
(2)如果class没有声明一个copyconstructor,就会有隐含的声明或隐含的定义出现,C++standard把copyconstructor区分为trivial何nontrivial两种,只有nontrivial的实体才会被合成于程序之中。决定一个copy constructor是否为trivial的标准在于class是否展现出所谓的”bitwisecopy semantics“。
(3)BitwiseCopy Semantics(逐位拷贝)
不要BitwiseCopy Semantics的情况有四种:
(a)当class内含一个memberobject而后者的class声明有一个copyconstructor时(不论是被class设计者明确地声明,还是被编译器合成);
(b)当class继承自一个baseclass而后者存在一个copyconstructor时(不论是被明确声明还是被合成而得);
(c)当class声明了一个或多个virtualfunctions时;
(d)当class派生自一个继承串链,其中有一个或多个virtual base class时。
前两种情况,编译器需将member或baseclass的copyconstructor调用操作安插到被合成的copyconstructor中,情况3和4有点复杂。
重新设定VirtualTable的指针:增加一个virtualfunction table(vtbl),内含每一个有作用的virtual function地址;将一个指向vtbl的指针(vptr),安插在每一个classobject内。
处理VirtualBase Class Subobject:一个classobject以另一个object作为初值,而后者有一个virtual base class subobject,也会使bitwisecopy semantics失效。
3、程序转化语意学
(1)返回值的初始化
已知函数定义:
X bar()
{
X xx;
//...
return xx;
}
bar()返回值如何从局部对象拷贝?
Stroustrup在cfront的解决方法是一个双阶段转化:
加上一个额外参数,类型是reference
在return前安插一个copy constructor操作
//函数转化
void bar(X& _result)//加上一个额外参数
{
X xx;
xx.X::X();//编译器产生的default constructor调用操作
//....处理xx
_result.X::XX(xx);//编译器产生的copyconstructor调用
return;
}
一个bar()调用被转化为:
X xx = bar();
转为:
X xx;
bar(xx);//注意不必调用default constructor
(2)在编译器层面做优化
以_result参数取代namedreturn value,称为NamedReturn Value(NRV)优化。由于NRV需要调用默认拷贝构造,如果编译器不生成,则需要手动添加拷贝构造。
(3)拷贝构造要还是不要?
如果class需要大量的memberwise初始化操作,例如以传值的方式传回value,则提供一个explicit copy constructor inline会更有效率。
4、成员的初始化队伍
必须使用member initialization list的情况:初始化一个reference member时;初始化const member;调用base class的constructor,而它拥有一组参数;调用member class的constructor,而它拥有一组参数。
使用成员初始化列表,可以提高效率,因为initializationlist的项目被放于explicituser code之前,但是要注意如果初始化某一项依赖于另一项时初始化的顺序,好的方法是将依赖某一项的成员放于constructor体内。