類的拷貝 賦值與銷毀

2021-08-04 10:08:12 字數 3923 閱讀 1122

拷貝建構函式、拷貝賦值運算子

移動建構函式、移動賦值運算子

析構函式

當定義乙個類時,我們顯式和隱式地指定在此型別物件的拷貝、移動、賦值和銷毀時做些什麼。而乙個類通過定義上述五種特殊的成員函式來控制這些操作。

拷貝和移動建構函式定義了當同型別的另乙個物件初始化本物件時做什麼。

拷貝和移動賦值運算子定義了將乙個物件賦予同型別的另乙個物件時做什麼。

析構函式定義了當此型別物件銷毀時做什麼。

1、拷貝建構函式

class foo ;
拷貝建構函式的第乙個引數是引用型別,而且此引數幾乎總是乙個const的引用。

合成拷貝建構函式(synthesized copy constructor)

如果我們沒有定義拷貝建構函式,編譯器則會為我們定義乙個合成拷貝建構函式。

一般情況下,合成拷貝建構函式將其引數的成員逐個拷貝到正在建立的物件中。編譯器從給定物件中依次將每個非static成員拷貝到正在建立的物件中。

每個成員的型別決定其如何拷貝:

類型別的成員---------呼叫其拷貝建構函式來拷貝

內建型別-------------直接拷貝

拷貝初始化

拷貝初始化和直接初始化的差異

string dots(10, ' ');   //直接初始化

string s(dots); //直接初始化

string s2 = dots; //拷貝初始化

string book = "9-999-99999-9"; //拷貝初始化

string nines = string(100, '9'); //拷貝初始化

當使用直接初始化時,我們實際上是要求編譯器使用普通的函式匹配來選擇與我們提供引數最匹配的建構函式。

而使用構造初始化時,我們要求編譯器將右側運算物件拷貝到正在建立的物件中,如果需要的話還要進行型別轉換。

拷貝初始化通常使用拷貝建構函式完成,有時也使用移動建構函式完成。

拷貝初始化除了在我們用 = 定義變數時會發生,在下列情況也會發生

將乙個物件作為實參傳遞給乙個非引用型別的形參 

從乙個返回型別為非引用型別的函式返回乙個物件用花括號列表初始化乙個陣列中的元素或乙個聚合類中的成員

例如:在函式呼叫過程中i,具有非引用型別的引數要進行拷貝初始化,類似的,當乙個函式返回具有非引用的返回型別時也會。

2、拷貝賦值運算子

與類控制其物件如何初始化一樣,類也可以控制其物件如何賦值。

sales_data trans, accum;

trans = accum; //使用sales_data的拷貝賦值運算子

合成拷貝賦值運算子

如果乙個類未定義自己的拷貝賦值運算子,編譯器會自動為其生成乙個合成拷貝賦值運算子。它會將右側運算物件的每乙個非static成員賦予左側運算物件。

合成拷貝賦值運算子返回乙個指向其左側運算物件的引用。

//等價於合成拷貝賦值運算子

sales_data&

sales_data::operator=(const sales_data &rhs)

3、析構函式

析構函式執行與建構函式相反的操作:建構函式初始化物件的非static資料成員,還可能做一些其他的工作:析構函式釋放物件使用的資源,並銷毀物件的非static資料成員。

如同建構函式有乙個初始化部分和乙個函式體,析構函式也有乙個函式體和乙個析構部分。在乙個建構函式中,成員的初始化是在函式體之前完成的,且按照它們在類中出現的順序初始化。在乙個析構函式中,首先執行函式體,然後銷毀成員。成員按初始化的順序逆序銷毀。

無論何時乙個物件被銷毀,就會自動呼叫其析構函式:

變數在離開其作用域時被銷毀

當乙個物件被銷毀時,其成員被銷毀

容器(無論是標準庫容器還是陣列)被銷毀時,其元素被銷毀

對於動態分配的物件,當指向它的指標應用delete運算子時被銷毀

對於臨時物件,當建立它的完整表示式結束時被銷毀

合成析構函式

當乙個類未定義自己的析構函式時,編譯器會自動為它分配乙個合成的析構函式

class sales_data 

//其他成員的定義,如前

};

認識到析構函式體自身並不直接銷毀成員是非常重要的。成員是在析構函式體之後隱含的析構階段中被銷毀的。在整個物件銷毀的過程中,析構函式體是作為成員銷毀步驟之外的另一部分進行的。

4、三五法則

需要析構函式的類也需要拷貝和賦值操作需要拷貝操作的類也需要賦值操作,反之亦然

5、使用 = default

我們通過將拷貝控制成員定義為 =default 來顯式地要求編譯器生成合成的版本。

class sales_data ;

sales_data& sales_data::operator=(const sales_data &) = default;

6、阻止拷貝

對於某些類來說,拷貝操作沒有合理的意義,在此情況下,定義類時必須採用某種機制阻止拷貝或賦值。例如,iostream類阻止了拷貝,以避免多個物件寫入或讀取相同的io緩衝。阻止拷貝,除了不定義拷貝控制成員外,還要考慮編譯器合成的版本。

定義刪除的函式

在新標準下,我們通過將拷貝建構函式和拷貝賦值運算子定義為刪除的函式來阻止拷貝。

struct nocopy ;

析構函式不能是刪除的成員

值得注意的是,我們不能刪除析構函式。如果析構函式被刪除,就無法銷毀此型別的物件了。對於乙個刪除了析構函式的型別,編譯器不允許定義該型別的變數或建立該型別的物件。而且,如果乙個類有乙個成員的型別刪除了析構函式,我們也不能定義該類的變數或臨時物件。

合成的拷貝控制成員可能是刪除的

對下列情況的類,編譯器將合成的成員定義為刪除的函式:

如果類的某個成員的析構函式是刪除的或不可訪問的(例如,是private的),則類的合成析構函式定義為刪除的。

如果類的某個成員的拷貝建構函式是刪除的或不可訪問的,則類的合成建構函式定義為刪除的。如果類的某個成員的析構函式是刪除的或不可訪問的,則類的合成建構函式定義為刪除的。

如果類的某個成員的拷貝賦值運算子是刪除的或不可訪問的,或類有乙個const的或引用成員,則類的合成拷貝賦值運算子定義為刪除的。

如果類的某個成員的析構函式是刪除的或不可訪問的,或類有乙個引用成員,它沒有類內初始化器,或類有乙個const成員,它沒有類內初始化器且型別未顯式定義預設建構函式,

則類的預設建構函式定義為刪除的。

本質上,這些規則的含義是,如果乙個類有資料成員不能預設構造、拷貝、複製或銷毀,則對應的成員函式將被定義為刪除的。

private拷貝控制

在新標準發布以前,類是通過將其拷貝建構函式和拷貝賦值運算子宣告為private的來阻止拷貝的。

拷貝,賦值與銷毀

拷貝控制操作基本概念 拷貝定義了當用同型別的另乙個物件初始化本物件時做什麼。賦值定義了將乙個物件賦予同型別的另乙個物件時做什麼。折構定義了當此型別物件銷毀時做什麼。拷貝建構函式 如果乙個建構函式的第乙個引數是自身型別的引用,且任何額外引數都有預設值,則此建構函式是拷貝建構函式。合成拷貝函式 如果我們...

c 中類的拷貝 移動 賦值 銷毀

第一次看c primer時拷貝控制這章完全沒看明白,只是很粗略地過了一遍。時隔一年,具備了一定的工程經驗後,再次看這一章對於類的拷貝構造 賦值函式 移動構造 賦值函式以及析構函式有了完全不一樣的認識,在部落格裡記錄一下,c primer每看一遍都會有不同的收穫倒真不是亂說。拷貝建構函式第乙個引數必須...

C 類的賦值和拷貝

include stdafx.h include include class a a a int id,char username id id username username a a a 過載拷貝函式 a operator a b 過載賦值函式,形參為引用,避免了一次物件的拷貝構造 或者 我們也...