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


最近读《深度探索C++对象模型》(下载见  ),满足了自己不少的好奇心。在此主要讨论下默认构造函数(default constructor) 和拷贝构造函数(copy constructor)的问题。

Default Constructor

首先以下几种情况下,编译器是不会自动合成默认构造函数的:

  1. 用户定义了其他带参数的构造函数(包括拷贝构造函数)
  2. 包含const成员
  3. 包含引用成员
那么其他情况下,如果用户没有定义任何构造函数,那么编译器是否一定会合成默认构造函数呢?

先看个例子

  1. class Node  
  2. {  
  3. public:   
  4.     int ID_;   
  5. };  
  6.   
  7.     Node node;  
  8.     func(node);  
  9. 003317BE  lea         eax,[node]    
  10. 003317C1  push        eax    
  11. 003317C2  call        func (331082h)    
  12. 003317C7  add         esp,4    
  13.   
  14.   
  15. class Node  
  16. {  
  17. public:   
  18.         string name_;  
  19.         int ID_;   
  20. };  
  21.   
  22.   
  23.     Node node;  
  24. 00104C7E  lea         ecx,[node]    
  25. 00104C81  call        Node::Node (10131Bh)    
  26.     func(node);  
  27. 00104C86  lea         eax,[node]    
  28. 00104C89  push        eax    
  29. 00104C8A  call        func (101082h)    
  30. 00104C8F  add         esp,4    

以上分别是两个类的定义和同样一段代码反汇编的结果,可以看到第一个例子并没有调用默认构造函数。

ISO C++ 2003有如下描述:

If there is no user-declared constructor for class X, a defaultconstructor is implicitly declared. Animplicitly-declareddefault constructoris an inline public member of its class. A constructor istrivialif it is an implicitly-declareddefault constructor and if:

— its class has no virtualfunctions (10.3) and no virtual base classes (10.1), and

— all the direct base classesof its class have trivial constructors, and

— for all the nonstatic datamembers of its class that are of class type (or array thereof), each such classhas a trivial constructor.

 

trivial default constructor 是个空的inline函数,而且很可能在生成代码时被优化掉了。

根据上面的描述,如果一个类满足以下条件,则编译器会合成nontrivial default constructor:

1. 声明(或继承)一个虚函数

          这种类需要一个虚函数表,因此编译器会在合成的默认构造函数里构造虚函数表并生成和初始化vtbptr

2.派生自一个继承链,其中有一个或多个虚基类

          编译器对虚基类的处理有很大差异,但其共同点是需要记录虚基类在每个派生类中的位置。

3. 其基类有nontrivial default constructor

          根据声明次序调用上一层基类的默认构造函数。

4. 包含带有默认构造函数的member class object

          以member在class中的声明次序来调用member的默认构造函数。

 

在上述情况下,如果设计者已经提供任何形式的构造函数,则编译器会扩张现有的每一个构造函数,

将相关代码加进去,而不会合成新的默认构造函数。


Copy Constructor

类似于默认构造函数,C++标准把拷贝构造函数也分为 trivial和nontrivial两种,只有nontrivial的实体才会被合成于程序之中。

只有class不展现出bitwise copy semantics时,才会合成一个nontrivial的拷贝构造函数.

bitwise copy semantics是指可以通过按位拷贝来复制一个类,那么显然包含指针成员时就不属于这种情形,

包含其他类对象时,则有可能不属于bitwise copy semantics。


具体来说有以下有四种情况不属于bitwise copy semantics

1.class内含一个成员对象,后者的class声明有一个拷贝构造函数(不论是用户明确声明,还是被编译器合成)

2.当class继承自一基类,而后者存在一个拷贝构造函数

3.当class声明一个或多个虚函数时

4.当class派生自一个继承链,其中有一个或多个虚基类。

上述4种情况与默认构造函数的情况基本一样,执行的操作也类似。


这里我们看到,当一个类不属于上述情况但包含一个指针成员时,编译器仍然会当作bitwise copy semantics来处理,

也就是直接拷贝指针,这也是导致很多bug的原因。

参考: 深度探索C++对象模型(Inside the C++ Object Model)下载见  

相关内容