最近讀《深度探索C++對象模型》(下載見 http://www.linuxidc.com/Linux/2012-03/56158.htm ),滿足了自己不少的好奇心。在此主要討論下默認構造函數(default constructor) 和拷貝構造函數(copy constructor)的問題。
Default Constructor
首先以下幾種情況下,編譯器是不會自動合成默認構造函數的:
先看個例子
以上分別是兩個類的定義和同樣一段代碼反匯編的結果,可以看到第一個例子並沒有調用默認構造函數。
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