《深度探索C++对象模型》--2 构造函数语意学

来源:互联网 发布:重度抑郁症的表现知乎 编辑:程序博客网 时间:2024/05/22 17:25

1default constructor的构造操作

  C++standard:对于class X,如果没有任何user-declared constructor,那么会有一个default constructor 被暗中(implicitly)声明出来,一个被暗中声明出来的default constructor将是一个trivial constructor但是这个生成的defaultconstructor只能满足编译器的需要,而不是程序的需要,所有的nonstaticdata member都不会被初始化,要注意。

  一个nontrivialdefault constructorARM的术语中就是编译器所需要的那种,必要的话会由编译器合成出来。下面讨论nontrivial defaultconstructor的四种情况。

1)带有defaultconstructormemberclass object如果一个类没有任何constructor,但它内含一个memberobject,而后者有defaultconstructor,那么这个classimplicitdefault constructor就是”nontrivial“的。

  既然编译器会合成默认构造函数,那么在C++各个不同的编译模块中,编译器如何避免合成出多个默认构造函数(比如一个是a.c文件,另一个是b.c文件)呢?解决办法是吧合成的default constructorcopyconstructordestructorassignmentcopy operator都以inline方式完成,一个inline函数有静态链接,不会被文件以外看到。如果函数太复杂,不适合做成inline,就会合成出一个explicitnon-inline static实体。

 如果class内含一个以上的memberclass objects,那么此class的每一个constructor必须调用每一个memberclassdefaultconstructor。编译器会扩张已经存在的constructor,在其中安插一些代码,似的user code被执行之前,先调用必要的defaultconstructor,且以声明顺序调用。

2)带有DefaultConstructorBaseClass如果一个没有任何constructorclass派生自一个带有defaultconstructorbase class,那么这个derivedclassdefaultconstructor会被视为nontrivial,并因此需要被合成出来。

3)带有一个VirtualFunctionClass两种情况

a)class声明(或继承)一个virtual function

 (b) class派生自一个继承串链,其中有一个或更多的virtual base classes

有两个扩张会在编译期间发生:一个virtualfunction table会被编译器产生出来,内放classvirtualfunctions地址;在每一个classobject中,一个额外的pointer member会被编译器合成出来,内含相关的class vtbl的地址。

4)带有一个VIrtualBase ClassClass: VirtualBase Class的实现法在不同的编译器之间有极大差异,然而,每一种实现法必须使virtualbase class在每一个derivedclass object中的位置,能够于执行期准备妥当。

2Copy Constructor的构建操作

1)当classobject以相同class的另一个object作为初值时,其内部是以所谓的default memberwise(按成员)initialization手法完成的。

2)如果class没有声明一个copyconstructor,就会有隐含的声明或隐含的定义出现,C++standardcopyconstructor区分为trivialnontrivial两种,只有nontrivial的实体才会被合成于程序之中。决定一个copy constructor是否为trivial的标准在于class是否展现出所谓的”bitwisecopy semantics“

3BitwiseCopy Semantics(逐位拷贝)

不要BitwiseCopy Semantics的情况有四种:

 a)当class内含一个memberobject而后者的class声明有一个copyconstructor时(不论是被class设计者明确地声明,还是被编译器合成);

 b)当class继承自一个baseclass而后者存在一个copyconstructor时(不论是被明确声明还是被合成而得);

 c)当class声明了一个或多个virtualfunctions时;

 d)当class派生自一个继承串链,其中有一个或多个virtual base class时。

前两种情况,编译器需将memberbaseclasscopyconstructor调用操作安插到被合成的copyconstructor中,情况34有点复杂。

重新设定VirtualTable的指针:增加一个virtualfunction tablevtbl),内含每一个有作用的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()返回值如何从局部对象拷贝?

Stroustrupcfront的解决方法是一个双阶段转化:

    加上一个额外参数,类型是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

barxx);//注意不必调用default constructor

2在编译器层面做优化

_result参数取代namedreturn value,称为NamedReturn ValueNRV)优化。由于NRV需要调用默认拷贝构造,如果编译器不生成,则需要手动添加拷贝构造。

3)拷贝构造要还是不要?

如果class需要大量的memberwise初始化操作,例如以传值的方式传回value,则提供一个explicit copy constructor inline会更有效率。

4、成员的初始化队伍

必须使用member initialization list的情况:初始化一个reference member时;初始化const member;调用base classconstructor,而它拥有一组参数;调用member classconstructor,而它拥有一组参数。

使用成员初始化列表,可以提高效率,因为initializationlist的项目被放于explicituser code之前,但是要注意如果初始化某一项依赖于另一项时初始化的顺序,好的方法是将依赖某一项的成员放于constructor体内。



0 0
原创粉丝点击