《深入探索C++对象模型》之二

前言:

       C++语言编译过程中,编译器会背着程序员做了很多工作,其中默认构造函数就是其中一件工作,透彻熟悉C++默认构造函数在何种情形下会生成,以及其作用是什么,对于C++程序员来是至关重要的。下面详细介绍C++构造语意学。

1、 Default Constructor的建造操作

        只有在编译器需要的时候才会合成一个default constructor,且被合成的default constructor只执行编译器所需的行动。C++ Standard中:“对于class X,如果没有任何user-declared constructor,那么会有一个default constructor被暗中(implicitly)声明出来......一个被暗中声明出来的default constructor将是一个trivial constructor.......”

         nontrivial default constructor生成的四种情况:

(1)带有Default Constructor的Member Class Object

        需要注意的是:如果class A内含一个或一个以上的member class objects,那么class A的每一个constructor必须调用每一个member classes的default constructor。如果有多个class member objects都要求constructor初始化操作,C++语言将要求以member objects在class中的声明次序来调用每个constructor。这一点由编译器完成。

(2)带有Default Constructor的Base Class

(3)带有一个Visual Function的Class

        另有两种情况,也需要合成出Default constructor:

1)class声明(或继承)一个visual function。

2)class派生自一个继承串链,其中有一个或更多的visual base class。

编译期间扩张操作包括:

1)一个virtual function table会被编译出来,内放class的virtual functions地址

2)在每一个class objects中,一个额外的pointer member(也就是vptr)会被编译器合成出来,内含相关的class vtbl的地址。

(4)带有一个visual base class的class

        小结:C++有两个误解:1)任何class如果没有定义Default constructor,就会被合成出一个来;2)编译器合成出来的Default constructor会明确设定“class 内每一个data member的默认值”。然而,事实上,C++合成默认构造函数只是在编译器需要的情况下才会合成,并且只有四种情况,C++ Standard把哪些合成物称为implicit nontrivial default constructor。它之所以能够完成任务,是借着“调用member object或base class的default constructor”或是“为每一个object初始化其visual function机制或visual base class机制”而完成。至于没有存在那四种情况而又没有声明任何constructor的classes,我们说它们拥有的是implicit trivial constructors,它们实际上并不会被合成出来。

        在合成的default constructor中,只有base class subobjects和member class objects会被初始化。所有其他的nonstatic data member均不会被初始化。这些初始化操作对程序而言或许有需要,但是对于编译器而言则并没有必要。

2、Copy Constructor的构造操作

        有三种情况,会以一个object的内容作为另一个class object的初值。

1)对一个object做明确的初始化操纵。

2)当object被当做参数交给某个函数。

3)当函数返回一个class object。

Default Memberwise Initialization:

        当class object以“相同class的另一个object”作为初值时,其内部是以所谓的default member initialization手法完成的,也就是把每一个内建的或派生的data member的值,从某个object拷贝一份到另一个object身上。不过它并不会拷贝其中的member class object,而是以递归的方式实施memberwise initialization。 决定一个copy Constructor是否为trivial的标准在于class是否展现出所谓的“bitwise copy semantics”。bitwise copy semantics(位逐次拷贝)意思就是:使用bitwise copy semantics时就不会使用default Constructor或copy Constructor。

bitwise copy semantics:

有四种情况不需要bitwise copy semantics:

1)当class 内含一个member object而后者的class声明有一个copy Constructor时。

2)当class继承自一个base class而后者存在有一个copy Constructor时。

3)当class声明了一个或多个virtual functions时。

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

        前两种情况中,编译器必须将member或base class的“copy Constructor调用操作”安插到被合成的“copy constructor"中。后两种情况中,存在virtual functions和virtual base classes,如果使用位逐次拷贝,其vptr就会出错。

后两种情况会出现重新设定virtual table的指针,只要有一个class声明了一个或多个virtual functions就会如此。

扩张操作:

1)增加一个virtual function table(vtbl),内含每一个有作用的virtual function的地址。

2)将一个指向virtual function table的指针(vptr),安插在每一个class object内。

        当一个编译器导入一个vptr到class之中时,该class就不再展现bitwise semantics了,故编译器需要合成一个copy constructor,以求将vptr适当地初始化。

3、 程序转化语意学

1)明确的初始化操作。

        必要的程序转化包括两个阶段:(1)重写每一个定义,其中的初始化操作会被剥夺;(2)class的copy constructor调用操作会被安插进去。

2)参数的初始化。

        其中一种策略:导入所谓的暂时性object,并调用copy constructor将它初始化,然后将该暂时性object交给函数,函数调用完成后,临时对象将会被析构。还有一种策略”拷贝构建“,将实际参数直接建造在其应该的位置上。

3)返回值的初始化。

named return value(NRV)优化机制。有时候看似不需要拷贝构造函数,但是为了NRV效率问题,也可以提供一个默认拷贝构造函数。

小结:拷贝构造函数的使用,迫使编译器对程序代码做部分优化,尤其当一个函数以传值(by value)的方式传回一个class object,而该class有一个copy constructor时,这将导致深奥的程序转化——不论在函数的定义或使用上。此外编译器也将copy constructor的调用操作优化,以一个额外的第一参数取代NRV。

4、成员们的初始化队伍(Member Initialization List)

当写一个constructor时,有机会设定class members的初值。要不是经由member Initialization list就是在构造函数内部进行数据成员的初始化。

下面四种情况必须使用成员初始化列表:

1)当初始化一个reference member时;

2)当初始化一个const member时;

3)当调用一个base class的constructor,而它拥有一组参数时;

4)当调用一个member class的constructor,而它拥有一组参数时。

        编译器会对Initialization list一一处理并可能重新排序,以反映出member的声明次序。它会安插一些代码到constructor体内,并置于任何explicit user code之前。

总结:

        主要介绍了default constructor、copy constructor还有何种情况使用到copy constructor以及成员初始化列表。其中default constructor只是在四种情况下才会被编译器构造出来,copy constructor和位逐次拷贝,在一些含有copy constructor的类中或者有virtual中,编译器需要合成copy constructor而不能使用位逐次拷贝,成员初始化列表在四种情况下必须使用。

参考文献:

[1]侯捷 《深入探索C++对象模型》


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值