1. 程式人生 > >深度分析C++預設建構函式、拷貝建構函式

深度分析C++預設建構函式、拷貝建構函式

對於C++初學者來說,時常不難看到他們說:
1.任何class如果沒有定義預設建構函式,那麼就會由編譯器來合成一個出來。
2.編譯器合成來的建構函式會明確確定裡面所有成員的值。(比如int型別成員會初始化成0)
呃,這當然是一部分C++新手的一廂情願吧、其實C++裡面對於建構函式的誕生與否是取決於編譯器是否需要必須要為其產生建構函式。。。或者說是有點類似被迫不得已時候,編譯器才會合成建構函式。這是第一點。
第二點,只有編譯器迫不得已時候,才會幫你初始化某些成員,而絕對不是所有成員都進行初始化。
無論如何也好吧,請記住這個詞:迫不得已時候。
舉個例子吧
class A{private:int mdata;}
就是這樣的類,假設沒有任何建構函式,那麼當我們宣告一個物件 A x;時候,會不會產生建構函式?會不會有mdata的0初始化發生?
答案是:沒有任何建構函式被合成,就算假設

有,好吧,我加粗了假設,再次強調是假設有,也不會初始化成0。還記得我們剛剛說的那個詞語麼?迫不得已,我們可以這樣想,C語言裡面 A x;這樣子的結構體也是可行的,那麼我們就沒有把編譯器逼到迫不得已的地步,或者想《深度探索C++物件模型》這本神書說的一樣,這是程式設計師的職責去寫建構函式,去初始化。這個和編譯器沒有半毛關係。。
那麼,什麼事情才算是迫不得已讓編譯器合成建構函式呢?建構函式的合成是類似什麼都不做?還是另有一番洞天? = = !
1.成員物件具有預設建構函式,這時候編譯器就無法再去偷懶了,生活已經把懶惰的它強迫到要去合成程式碼的地步了,怎麼說呢?或者我們可以這樣想吧。假設成員物件裡面有一個蛋疼的功能是申請堆資源。假設編譯器不幫這個類合成建構函式,那麼悲催的類中某一個成員物件生來就沒有申請到堆資源。。。
這樣就很莫名其妙的,對嗎?
呃,問題又來了,所謂的合成程式碼是值什麼合成呢?它是怎麼合成的呢?假設有1000個cpp檔案,每個檔案都有這個沒有建構函式的類的定義,都要用到這個定義的類,那麼編譯器是如何分別給1000個檔案去合成這個類的建構函式呢?呃,編譯器是這樣做的,如果檔案中存在著類物件的定義,那麼就必須要付出合成程式碼的代價。。。這時候採取的合成程式碼是儘量使用 inline方式合成。要是一些莫名其妙的程式碼,真的是無法用inline合成了,就採取static合成,呃,其實inline也好,static也好,都是為了避免產生多個全域性的符號。。。你懂得,1000個全域性符號,這個連結器估計也得受不了吧。。。目測得符號丟擲符號重定義錯誤,畢竟是1000個強符號啊…..
呃,問題又來了。。建構函式將會擴充成什麼樣子?或者這樣說吧。。。它初始化一些什麼東西?偷偷幫什麼成員進行了預設建構函式的調動。答案是:對內建型別,像什麼int double 鳥都不鳥他們,噢,那些沒有建構函式的類,看起來有點像C語言的結構體吧。。。C語言都不鳥它,那麼C’++幹嘛要鳥他?對吧。。。所以對於帶有建構函式的成員物件,才會被建構函式偷偷的初始化,
即使偷懶的程式設計師沒有初始化他們!!編譯器也得做!具體原因見上面再上面幾行加粗的字型。。。好好讀讀。編譯器是懶惰的,迫不得已時候才會對其進行改變。。。。
呃。。。這是最後一個問題了吧。。。建構函式函式對成員的初始化順序是和什麼有關的?A 建構函式引數列表?B 成員物件宣告次序?
答案是B。。。又到了要說為什麼然後解釋的時候。。。簡單的一個反證法就可以想出來了,假設是A,那麼對於一些不帶建構函式引數列表的類,是不是就不對帶有建構函式的成員物件初始化?這不科學吧???
2.帶有建構函式的基類。。道理有點類似啊,要是派生類繼承了基類,基類再建構函式裡面申請堆資源。。。那麼作為派生類,是否應該把應該屬於你的資源找回來????或者舉例子說吧,按照人的語言,公有繼承是表達 “是”的意思,假設老師繼承了人,人有吃飯的屬性,在建構函式裡實現了,然後老師繼承了人,但是沒有調動建構函式,老師變成了只會領取工資不會吃飯的人。。。。— —!
3.帶有虛指標的類
這個怎麼說呢,也是反證法!!!!!!假設編譯器傻不拉幾的把這個類忽略了,不再為它合成建構函式,那麼會有什麼後果?毫無疑問!!!!vfptr那些東東全部都是0xCCCCCCCC(棧)或者0xCDCDCDCD(堆)。。。。編譯器意思說,愚蠢的程式設計師,你自己來重置虛表吧!!!然後這個編譯器就被程式設計師輸入 kill -9殺掉了。。。好吧!!!重點來了!
兩個擴張操作會在編譯期間完成!
a.編譯器為了支援基類指標指向派生類物件,實現多型,必須要在編譯期把虛表這些都搞出來。。。地址填好
b.噢,每一個類物件裡面,都要給他們塞進一個指標,讓他們指向那個虛表。。。
記住了,我們編譯器為了不被程式設計師kill掉做了太多太多的東西了。。。真是可憐的編譯器
4.帶有虛基類的類,呃。虛繼承意思是共享繼承的意思。。。因為對應菱形繼承結構可能會存在C裡面有兩個X的類。。。呃,要是再往下資料冗餘會更加嚴重。。。這時候就引入了虛繼承這個概念了
這裡寫圖片描述
— — !長話短說!虛繼承破壞了純C的那個儲存結構,如果編譯器不按照一定的只能去恢復構造功能,那麼就將會很詫異了