通常c++初級程式設計師會認為當乙個類為沒有定義拷貝建構函式的時候,編譯器會 為其合成乙個,答案是否定的。編譯器只有在必要的時候在合成拷貝建構函式。 那麼編譯器什麼時候合成,什麼時候不合成,合成的拷貝建構函式在不同情況下 分別如何工作呢?這是本文的重點。
有乙個引數的型別是其類型別的建構函式是為拷貝建構函式。如下:
x::x(constx&
x);y::
y(consty&
y,int=0);
//可以是多引數形式,但其第二個即後繼引數都有乙個預設值
當乙個類物件以另乙個同類實體作為初值時,大部分情況下會呼叫拷貝建構函式。 一般是這三種具體情況:
後兩種情形會產生乙個臨時物件。
並不是所有未定義有拷貝建構函式的類編譯器都會為其合成拷貝建構函式,編譯 器只有在必要的時候才會為其合成拷貝建構函式。所謂必要的時刻是指編譯器在 普通手段無法完成解決「當乙個類物件以另乙個同類實體作為初值」時,才會合成 拷貝建構函式。也就是說,當常規**能解決問題的時候,就沒必要動用非常規 **。
如果乙個類沒有定義拷貝建構函式,通常按照「成員逐一初始化(default memberwise initialization)」的手法來解決「乙個類物件以另乙個同類實體作為 初值」——也就是說把內建或派生的資料成員從某乙個物件拷貝到另乙個物件身上, 如果資料成員是乙個物件,則遞迴使用「成員逐一初始化(default memberwise initialization)」的手法。
成員逐一初始化(default memberwise initialization)具體的實現方式則是位 逐次拷貝(bitwise copy semantics)1
。也就是說在能使用這種常規方式 來解決「乙個類物件以另乙個同類實體作為初值」的時候,編譯器是不需要合成拷 貝建構函式的。但有些時候常規**不那麼管用,我們就得祭出非常規**了 ——拷貝建構函式。有以下幾種情況之一,位逐次拷貝將不能勝任或者不適合來完 成「乙個類物件以另乙個同類實體作為初值」的工作。此時,如果類沒有定義拷貝 建構函式,那麼編譯器將必須為類合成乙個拷貝建構函式。
對於前兩種情況,不論是基類還是物件成員,既然後者宣告有拷貝建構函式時, 就表明其類的設計者或者編譯器希望以其宣告的拷貝建構函式來完成「乙個類物件 以另乙個同類實體作為初值」的工作,而設計者或編譯器這樣做——宣告拷貝構造函 數,總有它們的理由,而通常最直接的原因莫過於因為他們想要做一些額外的工 作或「位逐次拷貝」無法勝任。
對於有虛函式的類,如果兩個物件的型別相同那麼位逐次拷貝其實是可以勝任的。 但問題將出現在,如果基類由其繼承類進行初始化時,此時若按照位逐次拷貝來 完成這個工作,那麼基類的vptr將指向其繼承類的虛函式表,這將導致無法預料 的後果——呼叫乙個錯誤的虛函式實體是無法避免的,輕則帶來程式崩潰,更糟糕 的問題可能是這個錯誤被隱藏了。所以對於有虛函式的類編譯器將會明確的使被 初始化的物件的vptr指向正確的虛函式表。因此有虛函式的類沒有宣告拷貝構造 函式,編譯將為之合成乙個,來完成上述工作,以及初始化各資料成員,宣告有 拷貝建構函式的話也會被插入完成上述工作的**。
對於繼承串鏈中有虛基類的情況,問題同樣出現在繼承類向基類提供初值的情況, 此時位逐次拷貝有可能破壞物件中虛基類子物件的位置。
bitwise copy semantics 是default memberwise intializiation 的具體實現方式。也沒有在任何地方對這兩個名詞進行對比或者更多的闡述,這 一度使我疑惑,上面只是我個人的理解,你有任何不同的見解,歡迎你與我討論。
拷貝建構函式,預設拷貝建構函式
拷貝建構函式,預設拷貝建構函式 1.c 的預設拷貝建構函式,從深度拷貝和淺拷貝說起 c 類的預設拷貝建構函式的弊端 c 類的中有兩個特殊的建構函式,1 無參建構函式,2 拷貝建構函式。它們的特殊之處在於 1 當類中沒有定義任何建構函式時,編譯器會預設提供乙個無參建構函式且其函式體為空 2 當類中沒有...
建構函式 拷貝建構函式
建構函式可以分為三類 1 不帶引數的建構函式 在函式體中對資料成員賦初值,這種方式使該類的每乙個物件都得到同一組初值 2 帶引數的建構函式 如果使用者希望對不同的物件賦不同的初值,可以採用帶引數的建構函式。在呼叫不同物件的建構函式時從外面將不同的資料傳遞給建構函式,以實現初始化 3 複製建構函式 建...
建構函式 拷貝建構函式
person person 引用就是指標常量 所以person p person const p,引用所以一旦初始化指向某個位址,就不能在改變了。為什麼前面還要加const,因為拷貝建構函式的目的就是將乙個物件的所有屬性拷貝到另外乙個物件,這個物件的屬性不變,加上const 相當於 const pe...