歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

C++對象模型之構造函數

最近讀《深度探索C++對象模型》(下載見 http://www.linuxidc.com/Linux/2012-03/56158.htm ),滿足了自己不少的好奇心。在此主要討論下默認構造函數(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)下載見 http://www.linuxidc.com/Linux/2012-03/56158.htm 

Copyright © Linux教程網 All Rights Reserved