關於默認構造函數的幾個錯誤認識(四種情況下,編譯器會生成默認構造函數)

       書上提到沒有定義構造函數時進行顯式初始化成員,我很納悶,不是說沒有定義構造函數的時候會合成默認構造函數嗎,自己查了下,果然查到了一下轉載的內容:不是未定義構造函數就一定會合成默認構造函數的!
       假期間閒來無事,就下載了某大師的VC++視頻資料。在講到C++時,說是如果程序員沒有自己定義默認構造函數,那麼編譯器會自動爲我們產生一個默認的構造函數。 本來這個錯誤的認識很多程序員都有,不足爲奇。但有這麼多年編程經驗的高手也有這樣的錯誤認識就不禁讓我啞然了。
        其實編程語言和我們所用的任何軟件沒有區別,例如Photoshop、AutoCAD之類。其唯一不同的是我們用的編程語言是基於編譯器的,而應用軟件是基於我們的編程語言的。
        既然我們所用的軟件是基於編譯器的,那麼理解編譯器在背後到底爲我們做了些什麼、在什麼情況下做了哪些事情就顯得異常重要。這就像Photoshop會爲你產生一些基本圖形例如矩形、三角形之類,而不會憑空產生一些風景優美的圖片一樣。
        在《C++ Annotated Reference Manual(ARM)[ELLIS90]》中的Section 12.1告訴我們:"Default constructors...在需要的時候被編譯器產生出來"。
        其實默認構造函數也是分爲兩類的:有用的、無用的。
        所謂有用的標準也是就默認構造函數會爲我們的類做一些初始化操作。那麼無用的就不會做任何工作,從而對我們的類也就沒有任何意義。所以,我們通常所說的默認構造函數是指有用的默認構造函數,其英文名字叫nontrivial default constructor。
        那麼到底什麼時候編譯器會爲我們產生nontrivial default constructor呢?有下面四中情況:
       ①如果一個類裏面某個成員對象有nontrivial default constructor,編譯器就會爲我們的類產生nontrivial default constructor。
       那麼編譯器這樣做的理由是什麼?
       答案是因爲類成員對象有nontrivial default constructor,那麼編譯器就需要顯式的來調用這個類成員對象的nontrivial default constructor。而編譯器想顯式的調用類成員對象的nontrivial default constructor,就需要自己來合成一些代碼來調用。但是記住,編譯器合成的nontrivial default constructor僅僅調用類成員對象的默認構造函數,而不對我們類裏面的其它變量做任何初始化操作。
        也就是說,如果你想初始化類成員變量以外的變量例如一個int、一個String,那麼必須自己定義默認構造函數來完成這些變量的初始化。而編譯器會對你定義的默認構造函數做相應的擴展,從而調用類成員對象的nontrivial default constructor。
        ②如果一個派生類的基類有nontrivial default constructor,那麼編譯器會爲派生類合成一個nontrivial default constructor。
        編譯器這樣的理由是:因爲派生類被合成時需要顯式調用基類的默認構造函數。
        ③如何一個類裏面隱式的含有任何virtual function table(或vtbl)、pointer member(或vptr)。
        編譯器這樣做的理由很簡單:因爲這些vtbl或vptr需要編譯器隱式(implicit)的合成出來,那麼編譯器就把合成動作放到了默認構造函數裏面。所以編譯器必須自己產生一個默認構造函數來完成這些操作。
        所以如果你的類裏帶有任何virtual function,那麼編譯器會爲你合成一個默認構造函數。
        ④如果一個類虛繼承於其它類。
        編譯器這樣做的理由和③類似:因爲虛繼承需要維護一個類似指針一樣,可以動態的決定內存地址的東西(不同編譯器對虛繼承的實現不僅相同)。
        那麼除了以上四種情況,編譯器並不會爲我們的類產生默認構造函數。
        所以編程中切忌想當然,要明白哪些事情是編譯器做的,哪些事情需要程序員來完成的。就像堆所佔用的資源需要程序員自己來釋放,而棧空間是編譯器管理的一樣。
        只有如此,才能編寫出質量更高的代碼。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章