深度探索C++对象模型-构造函数语义学

来源:互联网 发布:rj45都有网络变压器吗 编辑:程序博客网 时间:2024/06/05 04:00

default constructor构建

  • default constructors在编译器需要的时候产生,区别于程序设计者的需要。以下程序进行说明:
class A(public: int v;);void test(){    A a;    //a.v没有初始化,需要设计者自行设计default constructor,编译器并不会合成    if(a.v)    //...}
  • 编译器会合成default constructor的四种情况:
    -带有default constructor的member class object
    -带有default constructor的base class(派生)
    -带有virtual base class的class
    -带有virtual function的class

  • 带有default constructor的member class object

class B{public:B(); //B constructor};class D{public:    B b;    int v;};void test{    D d; //d.b是member object, class B含有constructor    //...}

编译器会为class D合成包含member b初始化的代码。
如果D本身定义了constructor函数,如下:

inline D::D{ v = 0;}

那么合成的constructor函数如下:

inline D::D{    b.B::B(); //增加的代码    v = 0; //user code置于编译器增加代码后面
  • 带有virtual function的class
    c++对象模型通过vptr指针指向包含virtual function的表Vtbl实现虚函数访问。因此产生一个class object时,我们需要额外初始化vptr(内涵class vtbl地址),这部分编译器合成。
    出处:http://www.cnblogs.com/skynet/p/3343726.html
    c++对象模型
  • 带有virtual base class的class
class A{public: int i} ;class B : virtual public A;class C : virtual public A;class D : public B,public C;foo(const A *p) {pa->i=0;}void test(){    foo(nwe B);    foo(new C);}

编译器无法固定foo()中A::i的实际偏移位置,因此pa的具体类型在执行前位置。所以class D的内存布局中需要一个特殊指针指向其中A的位置。
这部分编译器的constructor和vptr的道理是一样的,只不过virtual base class A本身比较特殊(共享的),需要一个额外指针。
具体参见:http://blog.csdn.net/bluedog/article/details/4711169

copy constructor构建

  • 三种明显对使用copy constructor的情况
    -明确赋初值
class X; X b;X a = b;   //X a(b);

-明确赋初值作为参数传递给某个函数
-函数返回一个class object

  • Bitwise and memberwise copy
    在没有explicit copy constructor情况下,有时编译器可以通过简单的bitwise copy完成初始化化操作,而无需合成copy constructor。例如;
class Word{public:    Word(const char*);private:    int cnt;    char *str;}Word a("book");Word b(a); //bitwise copy

memberwise or bitwise copy:简单地将内建或者派生的data member从一个object拷贝到另一个object,而不会出错。即该class展现出“bitwise copy semantics”特性。

  • 需要合成copy constructor
    即该class不满足bitwise copy semantics”特性。有以下四种情况:
    -带有copy constructor的member class object
    -带有copy constructor的base class member(派生)
    -带有virtual function的class
    -带有virtual base class的class
  • 带有virtual function class
    如果采用bitwise copy,则object b将指向class D的virtual table,显然不对。所以编译器需要合成class D的copy constructor,明确将object的vptr指向class B的virtual table。
class Base{public:    virtual int a();}class Derive: public Base{public:    int a();}D d;Base b = d;   //需要编译器合成copy constructorD d2 = d;   //可以采用bitwise copy
  • 带有virtual base class的class
    编译器需要为class B合成一个copy constructor,设定object b中virtual base class pointer/offset的初值,有时只是简单确定它没有被消抹。确保正确!
class A{public: int i} ;class B : virtual public A;class C : public B;C c;C c2 = c; //bitwise copy即可B b = c;  //将其derived class的某个object作为初值时需要合成

程序转化语义学

  • 参数初始化 (函数参数)
    例如:
void foo(A a);  //函数声明A a;foo(a);  //object a作为实参传入实际上是copy constructor过程

其中的一种策略是导入暂时性object,如下。调用一次copy constructor, 函数调用结束,该暂时对象被destructor。另外,foo()函数声明也需要修改如下:
void foo(A &a);

    A __tmp0;    __tmp0.A::A(a);   //调用copy construction    //重写foo()函数调用,使用暂时对象,    foo(__tmp0); //__tmp0在函数中直接使用
  • 返回值的初始化
    具体实现也需要引入额外参数。
A foo(){    A x0;    //处理x0    return x0;}//具体实现机制A __return;void foo(A &__return){    x0.A::A(); //default constructor    //处理X0    __return.A::A(x0); //copy constructor     return;}
  • 编译器层面优化
    即Named Return Value(NRV)优化,NRV优化的目的是减少一次copy constructor。具体如下:
void foo(A &__return){    __return.A::A();  //default constructor    //直接处理__return,没有copy constructor    return;}

需要注意的是,NRV优化的本质是去掉copy constructor,去掉而非生成它,因此NRV优化的前提是该类有copy constructor。

  • 成员初始化队列(Member Initialization List)
    写一个constructor时,我们有机会设定class members的值,值得初始化在:1)Member Initialization List 2)constructor 函数体内。
    编译器会一一操作initialization list,然后再执行函数体内explicit user code。
    initialization list 的执行次序是class 中成员的申明次序,而非initialization中排列顺序。
    将成员初始化置于Member Initialization List是一种比较优化的方式。
Word::Word() : _name("hi") {_cnt = 0}//编译器扩张后的程序Word::Word(){    _name.String::String("hi"); //initialization list    _cnt = 0;  //explicit user code}

下面代码的初始化将导致无法预知的错误,由于i声明在j前面,因此i较j先初始化,而此时j的值无法预知。

class X{    int i;    int j;public:    X(int val): j(val), i(j){}}
0 0
原创粉丝点击