C++ primer中的三個地方講解了默認構造函數:
P44變量初始化規則
P227函數(構造函數)
P388類(構造函數初始化式)
P392默認構造函數
一, 變量初始化規則(P44和P227)
1,對於類類型的成員,調用該成員所屬類自身的默認構造函數實現初始化。
2,內置類型成員的初值依賴於對象定義的位置,如果對象在全局作用域中定義(既不在任何函數中)或定義為靜態局部對象,則這些成員將被初始化為0;
3,如果對象在局部作用域中定義,則這些成員沒有初始化!
二,初始化列表
由P49和P50可知:const和引用類型的成員,不能對它們賦值,所以必須在定義時初始化!
所以,對於const對象或引用類型的對象,在開始執行構造函數的函數體之前,要完成初始化!(P389)
初始化 const 或引用類型數據成員的唯一機會是在構造函數初始化列表中!
同時,由於沒有默認構造函數的類類型的成員,編譯器嘗試使用默認構造函數將會失敗,所以也必須在初始化列表中初始化!
三, 默認構造函數的定義
在P225定義了默認構造函數:沒有形參。
再看看百度百科的定義:默認構造函數(default constructor)就是在沒有顯式提供初始化式時調用的構造函數。它由不帶參數的構造函數,或者為所有的形參提供默認實參的構造函數定義。如果定義某個類的變量(對象)時沒有提供初始化式就會使用默認構造函數。
在P227中說:如果沒有為一個類顯示定義任何構造函數,編譯器將自動為這個類生成默認構造函數,通常稱為合成的默認構造函數,它一般用於僅包含類類型成員的類!它不會自動初始化內置類型或復合類型的成員。所以,對於含有內置類型或復合類型成員的類,通常應該定義他們自己的默認構造函數初始化這些成員。
問題:是不是所有的類都有默認構造函數?答案是否定的。
1, 雖然編譯器會為類自動生成一個合成的默認構造函數,但是它僅對於包含類類型成員的類。所以對於只含有內置類型或復合類型成員的類,如果不自定義他們的構造函數,編譯器不會自動為其生成一個合成的默認構造函數的!
class Sales_item{ public: ...... //Sales_item():units_sold(0), revenue(0.0){} private: //std::string isbn;//去掉它,編譯器就不會為本類自動生成默認構造函數! unsigned units_sold; double revenue; };
2, 當然,如果類顯示定義了默認構造函數,但是沒有為其類類型的成員在初始化列表中顯示初始化,那麼會調用該成員所屬類自身的默認構造函數實現初始化!這部分工作也是由編譯器自動添加到當前默認函數完成的!
class Sales_item{ public: ...... Sales_item():units_sold(0), revenue(0.0){}//如果為isbn成員也在這裡顯示初始化,那麼編譯器將忽略string的默認構造函數~ private: std::string isbn;//雖然顯示定義的默認構造函數,但是它的初始化是由編譯器自動添加完成的! unsigned units_sold; double revenue; };
3, 如果顯示定義帶參數的構造函數(任意的構造函數),那麼編譯器不會再生成默認構造函數!
(通常也會自動生成一個合成的默認構造函數,此時,類有兩個構造函數!同時編譯器也完成上面第2點的工作。這句話是×的,見P392)
class Sales_item{ public: ...... Sales_item(double rev):units_sold(0), revenue(rev){} private: std::string isbn; unsigned units_sold; double revenue; };
關於這一點需要上機測試(TODO:測試結果之後附上),因為百科上解釋如下:
一個類顯式地聲明了任何構造函數,編譯器不生成公有的默認構造函數。這這種情況下,如果程序需要一個默認構造函數,需要由類的設計者提供。
4,如果派生列的基類中有自定義的nontrivial default constructor,那麼編輯器會為每一個派生類合成一個nontrivial default constructor,以調用基類自定義的nontrivial default constructor。
5,如果一個類裡隱式的含有Virtual tabel(Vtbl)或者pointer member(vptr)
6,如果一個類虛繼承與其他類
編譯器會為每個有虛函數的類創建一個虛函數表,該虛函數表將被該類的所有對象共享。類的每個虛成員占據虛函數表中的一行。如果類中有N個虛函數,那麼其虛函數表將有N*4字節的大小。 虛函數(Virtual Function)是通過一張虛函數表(Virtual Table)來實現的。簡稱為V-Table。在這個表中,主要是一個類的虛函數的地址表,這張表解決了繼承、覆蓋的問題,保證其真實反應實際的函數。這樣,在有虛函數的類的實例中這個表被分配在了這個實例的內存中,所以,當用父類的指針來操作一個子類的時候,這張虛函數表就顯得由為重要了,它就像一個地圖一樣,指明了實際所應該調用的函數。 編譯器應該是保證虛函數表的指針存在於對象實例中最前面的位置(這是為了保證取到虛函數表的有最高的性能——如果有多層繼承或是多重繼承的情況下)。 這意味著可以通過對象實例的地址得到這張虛函數表,然後就可以遍歷其中函數指針,並調用相應的函數。