Inside the C++ Model第二讲之 Default Constructor的构造操作

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010772289/article/details/75807903

“带有Default Constructor”的Member Class Object
如果一个class没有任何constructor,但它内含一个member object, 而后者有default constructor,那么这个class的implicit default constructor就是”nontrivial”,编译器需要为该class合成出一个default constructor。不过这个合成操作只有在constructor真正需要被调用时才会发生。
举个例子,在下面的程序中,编译器为class Bar合成一个default constructor:

class Foo { public: Foo(), Foo( int ) ... };
class Bar { public: Foo foo; char *str; };
void foo_bar() {
    Bar bar; // Bar::foo must be initialized here
    if ( str ) { } ...
}

被合成的default constructor看起来像这样:

// possible synthesis of Bar default constructor
// invoke Foo default constructor for member foo
inline
Bar::Bar()
{
    // Pseudo C++ Code
    foo.Foo::Foo();
}

如果有多个class member objects都要求constructor初始化操作呢?C++语言要求以”member objects在class中的声明顺序”来调用各个constructors。这一点由编译器完成,它为每一个constructor安插程序代码,以”member声明顺序”调用每一个member所关联的default constructors。这些代码将被安插在explicit user code之前。

class Snow_White {
  public:
    Dopey dopey;
    Sneezy sneezy;
    Bashful bashful;
    // ...
  private:
    int mumble;
};
// 程序员所写的 default constructor
Snow_White::Snow_White() : sneezy( 1024 )
{
    mumble = 2048;
}

它会被扩张为:

// Compiler augmented default constructor
// Pseudo C++ Code
Snow_White::Snow_White()
{
    // insertion of member class object
    // constructor invocations
    dopey.Dopey::Dopey();
    sneezy.Sneezy::Sneezy( 1024 );
    bashful.Bashful::Bashful();
    // explicit user code
    mumble = 2048;
}

“带有Default Constructor”的Base Class
如果一个没有任何constructors的class派生自一个”带有default constructor”的base class,那么这个derived class的default constructor会被视为nontrivial,并因此需要被合成出来。
如果设计者提供了多个constructors,但其中都没有default constructor呢?编译器会扩张现有的每一个constructors,将所有必要的default constructor添加进去。

“带有一个Virtual Function”的Class
另外两种情况也需要合成出default constructor:
1.class声明(或继承)一个virtual function。
2.Class派生自一个继承串链,其中有一个或更多的virtual base classes。
例如:

class Widget {
  public:
    virtual void flip() = 0;
    // ...
};

void flip( const Widget& widget ) { widget.flip(); }

// 假设Bell和Whistle都派生自Widget
void foo() {
    Bell b; 
    Whistle w;
    flip( b );
    flip( w );
}

下面两个扩张行动会在编译期间发生:
1.一个virtual function table(vtbl)会被编译器产生出来,里面存放class的virtual functions的地址。
2.在每一个class object中,一个额外的pointer member(即vptr)会被编译器合成出来,来指出class vtbl的地址。
此外,widget.flip()的虚拟调用操作会被重新改写,以使用widget的vptr和vtbl的flip()条目:

//widget.flip()的虚拟调用操作的转变
( *widget.vptr[1] ) ( &widget )

其中:
■ 1表示flip()在virtual table中的固定索引
■ &widget代表要交给”被调用的某个flip()函数实例”的this指针。

为了让这个机制生效,编译器必须为每一个Widget(或其派生类的)object的vptr设定初值,放置适当的virtual table地址.对于class所定义的每一个constructors,编译器会安插一些代码来做这件事,而对于没有声明任何constructors的classes,编译器会合成一个default constructor,以便正确的初始化每一个class object的vptr.
上面这一段的意思就是说,如果一个class有虚函数,那么我们需要在构造时,安插vptr,设置virtual table的地址,因此需要在每一个构造函数都安插一些代码来做这件事,如果一个类没有构造函数,就为它合成一个来做这件事.

“带有virtual base class”的class
Virtual base class的实现在不同的编译器中差别很大.但,它们都需要保证使virtual base class在其每一个derived class object中的位置,在执行期能够准备好.

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; };

// cannot resolve location of pa->X::i at compile-time
void foo(const A* pa )  { pa->i = 1024; }

main() {
foo( new A );
foo( new C );
// ...
}

the compiler cannot fix the physical offset of X::i accessed through pa within foo(), since the actual type of pa can vary with each of foo()’s invocations. Rather, the compiler must transform the code doing the access so that the resolution of X::i can be delayed until runtime.

总结:
有4种情况,会造成“编译器必须为未声明constructor的classed合成一个default constructor”。C++ Standard把那些合成物称为implicit nontrivial default constructors。

上面说了这么多,感觉还是难以理解.整体意思就是说一个class如果没有定义任何的constructor,那么它会不会合成出一个default constructor来?这个是要看具体情况而定,default constructor分为trivial和nontrivial(trivial英文意思就是不重要的,琐碎的).对于trivial的default constructor,编译器不会合成,而nontrivial的default constructor是要合成的.那么哪些情况是nontrivial,也就是需要合成default constructor呢?就是前面说的4种情况,即:
1).一个class没有任何constructor,但它还有member object,而这个member object有default constructor.
2).一个class没有任何constructor,但它派生自一个”带有default constructor”的base class
3).一个class声明或继承了一个或多个virtual function
4).一个class派生自一个继承串链,其中有一个或更多的virtual base classes.
为什么这4种情况就是nontrivial呢?
1),对于上面的1)这种情况,在声明这个class的对象时,需要调用member object的默认构造函数,因此这个class会合成一个默认构造函数来初始化member object.如果这个class有多个member object需要初始化,它们是以member object在class中声明的顺序来分别初始化的.
另外,如果这个class定义了构造函数,需要调用member object的默认构造函数时,编译器也会扩张这个class的constructor,在其中安插一些代码,完成member object的初始化.
2).需要设置vptr.
3).对于3)这种情况,主要是需要在初始化时设置vptr,存放vptl的地址,因此,需要合成默认构造函数.
4).使virtual base class在其每一个derived class object中的位置,在执行期能够准备好.

猜你喜欢

转载自blog.csdn.net/u010772289/article/details/75807903
今日推荐