Default Constructor的构建操做

 在《C++ Annotated Reference Manual(ARM)[ELLIS90]》中的Section 12.1告诉咱们:"Default constructors...在须要的时候被编译器产生出来"。
其实默认构造函数也是分为两类的:有用的(nontrivial )、无用的(trivial )。
所谓有用的标准也是就默认构造函数会为咱们的类作一些初始化操做。那么无用的就不会作任何工做,从而对咱们的类也就没有任何意义。
因此,咱们一般所说的默认构造函数是指有用的默认构造函数,其英文名字叫nontrivial default constructor。
例以下面的代码:
class Foo {
public: 
    int val;
    Foo *pnext;
};

void f00_bar()
{
    Foo bar;  //程序要求bar's members都被清为零,不是编译器须要
    if(bar.val || bar.pnext)
        //...do something
}

编译器并不会为上述代码生成一个默认的构造函数,只有在下面四种状况下,编译器才会生成默认构造函数(nontrivial default constructor):程序员

(1) 若是一个类里面某个成员对象有nontrivial default constructor,编译器就会为咱们的类产生nontrivial default constructor数组

class Foo {
 public:
    Foo();
    Foo(int);
    ...
};

class Bar {
public:
    Foo foo;
    char *str;
};

void foo_bar()
{
    Bar bar;
    if(str) {}
    ...
}

 被合成的Bar default constructor 内含必要的代码,可以调用class Foo的default constructor 来处理member object Bar::foo,但它并不产生任何代码来初始化Bar::str。被合成的default constructor看起来可能像这样:bash

inline Bar::Bar()
{
    //C++伪代码
    foo.Foo::Foo();
}

若是程序员定义了default constrcutor函数

Bar::Bar() 
{
    str = 0;
}

如今程序员的需求知足了,可是编译器还须要初始化member object foo。编译器会扩张现存的constructor,在其中安插一些代码,使得user code在被执行前,先调用必要的default constructors。设计

Bar::Bar()
{
    foo.Foo::Foo(); //附加上的compiler code
    str = 0;
}

若是有多个class member objects都要求constructor初始化操做,C++语言要求以 “member objects在clsss中的声明次序”来调用各个constructor。指针

class Dopey   {public: Dopey(); ...};
class Sneezy  {public: Sneezy(int); Sneezy(); ...};
class Bashful  {public: Bashful(); ...};

class Snow_White{
public:
    Dopey dopey;
    Sneezy Sneezy;
    Bashful bashful;
    ....
private:
    int mumbae;
};

若是Snow_White没有定义default constructor,就会有一个nontrivial constructor被合成出来,一次调用Dopey、Sneezy、Bashful的default constructor。若是定义了下面这样的default constructor:code

Snow_White::Snow_White() : sneezy(1024)
{
    mumble = 2048;
}

他会被扩展为:

Snow_White::Snow_White()
{
  //按照声明的顺序调用其constructor
    dopey.Dopey::Dopey();
    sneezy.Sneezy::Sneezy();
    bashful.Bashful::Bashful();

    mumble = 2048;
}

(2) 若是一个派生类的基类有nontrivial default constructor,那么编译器会为派生类合成一个nontrivial default constructor对象

编译器这样的理由是:由于派生类被合成时须要显式调用基类的默认构造函数。blog

若是设计者提供多个constructor,但其中都没有调用default constructor,编译器不会合成一个新的default constructor,它会扩张现有的每个constructor,将“用以调用全部必要之default constructor”的程序代码加进去。继承

(3) 若是一个类带有一个Virtual Function,那么编译器会为派生类合成一个nontrivial default constructor

编译器这样作的理由很简单:由于这些vtbl或vptr须要编译器隐式(implicit)的合成出来,那么编译器就把合成动做放到了默认构造函数里面。因此编译器必须本身产生一个默认构造函数来完成这些操做。
因此若是你的类里带有任何virtual function,那么编译器会为你合成一个默认构造函数。
(4) 若是一个类虚继承于其它类,那么编译器会为派生类合成一个nontrivial default constructor

编译器这样作的理由和(3)相似:由于虚继承须要维护一个相似指针同样,能够动态的决定内存地址的东西(不一样编译器对虚继承的实现不全相同)。

class X {public: int i; };
class A : public virtual X {public: int j; };
class B : public virtual X {public: double d; };
class C : public A, public B {public: int k; };


//没法在编译时期决定出pa->X::i 的位置
void foo(const A* pa) { pa->i = 1024; }

mian()
{
    foo(new A);
    foo(new C);
    ...
}

编译器没法固定住foo()之中“经由pa而存取的X::i”的实际偏移量,觉得pa的真正类型能够改变。foo()能够被改写以下:

void foo(const A* pa) { pa->_vbcX->i = 1024; }

其中,_vbcX表示编译器所产生的指针,指向virtual base class X。  

总结

上面四种分析合成出的default constructor都是nontrivial default constructors,不在此状况以内的都trivial default constructors,它们实际上并不会被编译器合成出来
在合成的default constructors,只有base class subobjects和member class object会被初始化,全部其它的nonstatic data member,如整数、整数指针、整数数组等等都不会被编译器初始化。
C++新手常见的两个的 错误
(1)  任何class 如何没有定义default constructor,就会被合成出来一个
(2)  编译器合成出来的default constructor会明确设定class 内每一个data member 的默认值
相关文章
相关标签/搜索