深入探索C++对象模型(二)构造函数语义学

1. 默认构造函数会在需要的时候被编译器产生出来,要注意字眼“需要的时候”,例如如下代码:

[cpp]  view plain   copy
  1. class Bat{  
  2. public:  
  3.     int val;  
  4.     Bat* pNext;  
  5.     void bat_yell();  
  6. };  
  7.   
  8. void Bat::bat_yell()  
  9. {  
  10.     Bat batMan;  
  11.     if(batMan.val || batMan.pNext)  
  12.         // ...do something  
  13.     // ...        
  14. }  

按照程序员的本意,在bat_yell()方法中,batMan会调用默认构造函数将val和pNext初始化为0,但是实际结果并不是这样,程序员的需要并不代表编译器需要,当然,程序员的责任也不该被比那一起所承担,归根结底说来,上述代码片段中,并不会有默认构造函数被合成。退一步讲,即便是有默认的构造函数被合成出来了,也不会将上述的数据成员初始化为0。也就是说此时的默认构造函数是trivial (无用的,不重要的),有四种情况编译器会产生nontrivial(重要的,有用的)的默认构造函数:

a. 带有默认构造函数的类成员对象:


Bar的类成员对象foo含有默认构造函数,所以编译器会为类Bar合成默认构造函数,可能看起来像下面这样:

值得注意的是,即便是编译器为我们合成了默认构造函数,但是并不会产生任何代码来初始化Bar的str成员变量,这个是程序员的责任。那么程序员如果提供了构造函数(默认或者带参数的),编译器会做些什么呢?答案是编译器会扩展以后的构造函数,在每一个构造函数中安插代码,代码会被安插到程序员代码的前面,使得每一个构造函数调用每一个带有默认构造函数的成员变量的默认构造函数,有一点绕,看个具体例子:

b. 第二种情况是基类中由默认构造函数,类似于第一种情况,如果一个没有定义默认构造函数的类继承(派生)于一个带有默认构造函数的基类,那么这个子类的默认构造函数会被合成出来,在其中以按照声明顺序分别调用基类们的构造函数。如果类设计者提供了多个构造函数,但是没有默认构造函数,编译器会扩张已有的构造函数,而不会合成一个新的默认构造函数。如果此类还包含第一种情况:含有存在默认构造函数的成员类对象,那么他们的默认构造函数会在调用基类默认构造函数之后调用。
c. 带有虚函数的类,由于虚函数的存在,会产生虚函数表和虚表指针,而这些必须在对象构造期间安排好,所以对于类所定义的每一个构造函数,编译器会安插一些代码来做这样的事情,对于未声明任何构造函数类,编译器会为他们产生一个默认的构造函数,以便正确的初始化每一个类对象的虚表指针。
d. 继承自虚基类的类,由于在虚拟继承下子类会存在指向虚基类的指针,与第三种情况类似。

总之,以上四种情况编译器会合成一个隐式的重要的默认构造函数(implicit nontrivial default constructor)来满足编译器(而非程序)的需要:调用成员类或者基类的默认构造函数,或者完成每一个对象初始化其虚函数机制或者虚基类机制。对于其他情况并且没有声明任何构造函数的类,他们拥有的是隐式的不重要的默认构造函数(implicit trivial default constructor),实际上并不会被合成出来。
一定要注意,合成出来的默认构造函数只会初始化编译器认为的必要的部分(基类对象,类对象),其他数据成员并不会被初始化。


Default Constructor的构造操作

对于class X,如果没有任何user-declared constructor,那么会有一个default constructor被隐式(implicitly)声明出来...一个被隐式声明出来的default constructor将是一个trivial(浅薄而无能,没啥用的)constructor...

一个nontrivial default constructor在ARM(注释参考手册)的术语中就是编译器需要的那种,必要的话由编译器合成出来。下面4小节分别讨论nontrivial default constructor的4种情况

“带有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必须在此初始化
    if(str) { } ... 
}

被合成的Bar default constructor内含必要的代码,能够调用class Foo的default constructor来处理member Bar::foo,但它并不产生任何码来初始化Bar::str。将Bar::foo初始化时编译器的责任,将Bar::str初始化则是程序员的责任。

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

“带有Default constructor”的base class

如果一个没有任何constructors的class派生自一个“带有default constructor”的base class,那么这个derived class的default constructor会被视为nontrivial,并因此需要被合成出来。它将调用上一层base classes的default constructor(根据它们的声明的顺序)。对于一个后继派生的class而言,这个合成的constructor和一个“被显式提供的default constructor”并没有差异

“带有一个Virtual Funtion”的class
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值