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

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 constructor
D 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值