天天看點

C++拷貝構造函數

對于普通類型的對象來說,它們之間的複制是很簡單的,例如:

int a=88;

int b=a;

而類對象與普通對象不同,類對象内部結構一般較為複雜,存在各種成員變量。

1、對象的定義形式

c++支援兩種定義形式:直接初始化和複制初始化,複制初始化采用=符号,直接初始化将初始化式放在圓括号中

用于類類型對象時,直接初始化直接調用與實參比對的構造函數,複制初始化總是調用拷貝構造函數,複制初始化首先使用指定構造函數建立一個臨時對象,然後拷貝構造函數将那個臨時對象拷貝到正在建立的對象

通常,直接初始化和複制初試化隻在低級别優化上存在差異,但是對于不支援複制的類型,或者使用非explicit構造函數的時候,它們就有本質的差別:

2、形參與傳回值

當形參是非引用類型的時候,将複制實參的值,以非引用類型作為傳回值的時候,将傳回return語句中值的副本

3、初始化容器元素

複制初始化函數可用于初始化順序容器中的元素,容器的這種構造方式使用了預設構造函數和拷貝構造函數:

vector<string> svec(5);

4、構造函數與數組元素

如果沒有為類類型數組提供元素的初始化形式,則将使用預設構造函數初始化每個元素,然而,如果使用正常的花括号包覆的數組初始化清單來提供顯示元素的初始化式,則使用複制初始化來初始化每個元素:

如果沒有定義拷貝構造函數,編譯器會為我們合成一個,即使我們定義了其他的構造函數,也會合成拷貝構造函數。合成拷貝構造函數的行為是,執行逐個成

員的初始化。編譯器将現有對象的每個非static成員,依次複制到正建立的對象,合成拷貝構造函數直接複制内置類型成員的值,類類型成員使用該類的拷貝

構造函數進行複制,數組成員使用合成拷貝構造函數将複制數組的每一個元素

定義自己的拷貝構造函數

下面看一個類對象拷貝的簡單例子:

運作程式,螢幕輸出100。從以上代碼的運作結果可以看出,系統為對象a1配置設定了記憶體并完成了與對象a的拷貝過程。就類對象而言,相同類型的類對象是通過拷貝構造函數來完成整個複制過程的。下面舉例說明拷貝函數的工作過程:

a(const a& c)就是我們自定義的拷貝構造函數。可見,拷貝構造函數是一種特殊的構造函數,函數的名稱必須和類名稱一緻,它的唯一的一個參數是本類型的一個引用變量,該參數是const類型,不可變的。例如:類x的拷貝構造函數的形式為x(x& x)。

當用一個已初始化過了的自定義類類型對象去初始化另一個新構造的對象的時候,拷貝構造函數就會被自動調用。也就是說,當類的對象需要拷貝時,拷貝構造函數将會被調用。以下情況都會調用拷貝構造函數:

一個對象以值傳遞的方式傳入函數體

一個對象以值傳遞的方式從函數傳回

一個對象需要通過另外一個對象進行初始化

如果在類中沒有顯式地聲明一個拷貝構造函數,那麼,編譯器将會自動生成一個預設的拷貝構造函數,該構造函數完成對象之間的位拷貝。位拷貝又稱淺拷貝,後面将進行說明。自定義拷貝構造函數是一種良好的程式設計風格,它可以阻止編譯器形成預設的拷貝構造函數,提高源碼效率。

 在某些狀況下,類内成員變量需要動态開辟堆記憶體,如果實行位拷貝,也就是把對象裡的值完全複制給另一個對象,如a=b。這時,如果b中有一個成員

變量指針已經申請了記憶體,那a中的那個成員變量也指向同一塊記憶體。這就出現了問題:當b把記憶體釋放了(如:析構),這時a内的指針就是野指針了,出現運作

錯誤。

 深拷貝和淺拷貝可以簡單了解為:如果一個類擁有資源,當這個類的對象發生複制過程的時候,資源重新配置設定,這個過程就是深拷貝,反之,沒有重新配置設定資源,就是淺拷貝。下面舉個深拷貝的例子:

深拷貝和淺拷貝的定義可以簡單了解成:如果一個類擁有資源(堆,或者是其它系統資源),當這個類的對象發生複制過程的時候,這個過程就可以叫做深拷貝,反之對象存在資源,但複制過程并未複制資源的情況視為淺拷貝。

淺拷貝資源後在釋放資源的時候會産生資源歸屬不清的情況導緻程式運作出錯。

拷貝構造函數的名稱必須與類名稱一緻,函數的形式參數是本類型的一個引用變量,且必須是引用。當用一個已經初始化過了的自定義類類型對象去初始化另一個新

構造的對象的時候,拷貝構造函數就會被自動調用,如果你沒有自定義拷貝構造函數的時候,系統将會提供給一個預設的拷貝構造函數來完成這個過程

禁止複制

拷貝構造函數是私有的,将不允許使用者代碼複制該類類型的對象,但是類的友元和成員仍可以進行複制,如果連類的友元和成員也禁止,可以聲明一個private構造函數但是不對其定義

繼續閱讀