C++ 对象模型之构造函数

看看以下这段代码:

1
2
3
4
5
6
7
8
9
class Foo { public: int val; Foo *pnext; };

void foo_bar()
{
    Foo bar;
    if (bar.val || bar.pnext )
        // ... do somthing
    // ...
}

上述程序并不会合成出一个 default constructor。什么时候会合成出 default constructor 呢,下面分4种情况。

带有 Default Constructor 的 Memeber Class Object

编译器需要为该 class 合成一个 default constructor,不过这个合成操作只有在 constructor 真正需要被调用时才会发生。

合成的 default constructor、copy constructor、destructor、assignment copy operator 都以inline方式完成,如果函数太复杂,不适合做成inline,就会合成出 explicit non-inline static 实例。

举个例子:

1
2
3
4
5
6
7
class Foo { public: Foo(); Foot(int ) ... };
class Bar { public: Foo foo; char *str; };

void foo_bar()
{
    Bar bar;
}

对象关系

编译器会为class Bar合成一个 default constructor 来处理 Bar::foo,但它并不初始化Bar::str

合成的 default constructor 可能像这样:

1
2
3
4
5
inline
Bar::Bar()
{
    foo.Foo::Foo();
}

假设程序员提供了 default constructor:

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

则编译器会扩张已存在的 constructors:

1
2
3
4
5
Bar::Bar()
{
    foo.Foo::Foo();
    str = 0;
}

如果有多个 class member objects 都要求 constructor 初始化操作,C++ 语言将以 member objects 在 class 中的声明顺序来调用各个 constructors。

带有 Default Constructor 的 Base Class

将会合成 Default Constructor,会根据 base class 声明的顺序调用 base class 的 default constructor。

如果有多个 constructors,编译器会扩张现有的每一个 constructors。

带有一个 Virtual Function 的 Class

以下两种情况,也需要合成出 default constructor:

  1. class 声明(或继承)一个 virtual function。
  2. class 派生自一个继承串链,其中有一个或更多的 virtual base classes。

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Widget {
public:
    virtual void flip() = 0;
    // ...
};

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

void foo()
{
    Bell b;
    Whistle w;
    
    flip(b);
    flip(w);
}

类图

编译期间发生两个扩张:

  1. virtual function table
  2. pointer member (也就是 vptr )

flip 函数可能被改写如下:

1
( *widget.vptr[1] )( &widget )

为了让这个机制发挥功效,编译器必须为每一个 Widget(或其派生类)object 的 vptr 设置初值,放置适当的 virtual table 地址。对于 class 所定义的每一个 constructor,编译器会安插一些代码来做这样的事情,如果没有 construcotr,则合成一个。

带有一个 Virtual Base Class 的 Class

例如以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
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; };

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

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

foo()可能被改写如下:

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

class 所定义的每一个 constructor,编译器会安插代码来初始化_vbcX,如果没有 constructors,编译器必须合成一个 default constructor。

总结

C++ 新手一般有两个常见的误解:

  1. 任何 class 如果没有定义 default constructor,就会被合成出一个来。
  2. 编译器合成出来的 default constructor 会显式设定 class 内每一个 data member 的默认值。

如你所见,没有一个是真的。