歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> C++對象模型之構造函數

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

日期:2017/3/1 10:27:12   编辑:Linux編程

最近讀《深度探索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. Node node;
  7. func(node);
  8. 003317BE lea eax,[node]
  9. 003317C1 push eax
  10. 003317C2 call func (331082h)
  11. 003317C7 add esp,4
  12. class Node
  13. {
  14. public:
  15. string name_;
  16. int ID_;
  17. };
  18. Node node;
  19. 00104C7E lea ecx,[node]
  20. 00104C81 call Node::Node (10131Bh)
  21. func(node);
  22. 00104C86 lea eax,[node]
  23. 00104C89 push eax
  24. 00104C8A call func (101082h)
  25. 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