函数"/>
【C++对象模型】构造函数
构造函数语意学
默认构造函数的构造操作
C++Annotated Reference Manual告诉我们:“默认构造函数……在需要的时候被编译器产生出来”。关键字眼是“在需要的时候”。被谁需要?做什么事情?
看看下面这段程序代码:
class Foo{
public:int value;Foo* pnext;
};void foo_bar()
{Foo bar;if(bar.value || bar.pnext)...
}
有一个默认构造函数,可以将它的两个members初始化为0。上面这段代码可曾符合ARM所说的“在需要的时候”?答案是 no。其间的差别在于一个是程序的需要,一是编译器的需要。程序如果有需要,那是程序员的责任;本例要承担责任的是设计class Foo的人
那么,什么时候才会合成出一个默认构造函数呢?当编译器需要它的时候!此外,被合成出来的构造函数只执行编译器所需的行动。也就是说,即使有需要为class Foo合成一个默认构造函数,那个构造函数也不会将两个val和pnext初始化为0。为了让上一段代码正确执行,class Foo的设计者必须提供一个显式的默认构造函数,将两个members适当地初始化。
“带有默认构造函数”的成员类对象
编译器需要为该 class 合成出一个默认构造函数。不过这个合成操作只有在构造函数真正需要被调用时才会发生。
在C++各个不同的编译模块中,编译器为了避免合成出多个默认构造函数(比如说一个是为A.C文件合成,另一个是为B.C文件合成)把合成的默认构造、拷贝构造、析构、赋值复制运算符都以inline方式完成。一个inline函数有静态链接,不会被文件以外者看到。如果函数太复杂,不适合做成inline,就会合成出一个explicit non-inline static实例
class Foo{public:int value;};
class Bar{public:Foo foo; char* str}void foo_bar()
{Bar bar; //bar::foo在此处初始化if(str)...
}
被合成的Bar默认构造函数内含必要的代码,能够调用class Foo的默认构造函数来处理member object Bar::foo
将Bar::foo初始化是编译器的责任,将Bar::str初始化则是程序员的责任。
被合成的默认构造函数只满足编译器的需要,而不是程序的需要。
如果class A内含一个或一个以上的member class objects,那么 class A的每一个构造函数必须调用每一个member classes 的默认构造函数 。编译器会扩张已存在的构造函数,在其中安插一些代码
如果有多个class member objects都要求构造函数初始化操作,C++语言要求以“成员对象 在class中的声明顺序”来调用各个构造函数。
“带有默认构造函数”的 Base Class
如果一个没有任何构造函数的class派生自一个“带有默认构造函数”的基类,那么这个继承类的默认构造函数会被视为nontrivial(不平凡),并因此需要被合成出来。它将调用上一层base class的默认构造函数
如果设计者提供多个构造函数,但其中都没有默认构造函数呢?编译器会扩张现有的每一个构造函数,将“用以调用所有必要之默认构造函数”的程序代码加进去。
“带有一个虚函数”的Class
另有两种情况,也需要合成出默认构造函数:
- class声明(或继承)一个虚函数。
- class派生自一个继承串链,其中有一个或更多的虚基类。
下面两个扩张行动会在编译期间发生:
- 一个 虚函数表(vtbl)会被编译器产生出来,内放 class的虚函数地址。
- 在每一个类对象中,一个额外的虚函数指针(vptr)会被编译器合成出来,内含相关之虚函数表的地址。
此外,基类的虚拟调用操作会被重新改写,以使用子类的vptr和vtbl中的条目为了让这个机制发挥功效,编译器必须为每一个基类的vptr设定初值,放置适当的虚函数表地址。对于class所定义的每一个构造函数,编译器会安插一些代码来做这样的事情
“带有一个虚基类”的 Class
//菱形继承
class X {public :int i;};
class A :public virtual X {public :int j;};
class B :public virtual X {public :double d;};
class B :public A , public B{public :int k;};void foo(const A* pa){ pa -> i = 1024;}
编译器无法固定住fo()之中“经由pa而存取的X::i”的实际偏移位置,因为pa的真正类型可以改变。
foo()可以被改写如下,以符合这样的实现策略
void foo(const A* pa){ pa -> __vbcX ->i = 1024;}
其中__vbcX表示编译器所产生的指针,指向虚基类X。
更多推荐
【C++对象模型】构造函数
发布评论