C 之物件的初始化 複製和銷毀

2022-06-06 00:57:17 字數 3883 閱讀 8400

c++定義了幾種不同的初始化形式,對於類型別的物件來說,不同的初始化形式意味著要呼叫不同的建構函式。

初始化方式:預設初始化,直接初始化,拷貝初始化,列表初始化

預設初始化:如果定義物件時沒有指定初值,物件被預設初始化,呼叫類中的預設建構函式,例:a a; //預設初始化,呼叫a() a arr[2]; //呼叫兩次a() 初始化陣列每個元素

a arr[2]; 初始化陣列每個元素**如下:

class a

」中的初始值構造物件,呼叫相應的建構函式,與直接初始化類似,花括號可以是初始值列表,用來初始化陣列的每個元素,此時對每個值呼叫建構函式,建立陣列元素:如果初始值的個數少於陣列大小,對後面的元素呼叫預設建構函式初始化。

建構函式:

預設建構函式:如果乙個類沒有定義任何建構函式,編譯器會在需要時自動合成乙個預設建構函式,類中一旦定義了建構函式,即使不是預設建構函式,編譯器也不再合成。

拷貝建構函式

有時候會用自己已有的物件去初始化另乙個同型別的物件:

例:x one;

x two(one); //用one初始化同型別的物件two

用one 初始化two 時需要建構函式x(x&),稱為拷貝建構函式

如果在類中沒有定義這樣的建構函式,編譯器會自動合成乙個,預設的行為是逐個成員複製。x two(one)就是用one 中的每個成員分別去初始化two 的每個對應的成員,這種行為也稱為淺複製(shallow copy)

淺複製在使用上有它的侷限性:在簡單類中,只包含內建int與陣列arr和string成員等,這種按成員複製的預設行為沒有出現錯誤,但是涉及到指標或者引用時,淺複製的該預設行為便不再恰當。為了避免編譯器合成的不恰當的淺複製行為可以自己定義拷貝建構函式,對包括指標和引用在內的成員進行恰當的初始化。我們把這種與淺複製相對立的行為叫做深複製。

下面舉例進行說明:

例:指標和引用成員的淺複製:

深複製與拷貝建構函式:

class x 

...};

拷貝建構函式的一般形式是x(x&)或者x(connst x&):其中x(x&)不能用const物件來初始化另乙個x型別的物件。

例:

//如果拷貝建構函式為x(x&)

const x a;

x b(a); //錯誤: 沒有匹配的函式'x::x(const x&)'

//如果拷貝建構函式為x(const x&)

const x a;

x b(a); //正確

x c(b); //正確

另外拷貝建構函式的形式不會是x(x)或者x(x*),原因:

如果用x 類的物件a 初始化x 類的物件b,會引起拷貝建構函式的呼叫,但在呼叫x(x obj)時,按值傳引數是用實參a 初始化形參obj,這同樣是用乙個物件初始化另乙個同類物件,又需要呼叫拷貝建構函式,重複這一過程,顯然會陷入對x(x)的無限迴圈呼叫,因此,拷貝建構函式的形參不會是x(x)

用物件初始化另乙個物件,不是用位址,因此不會是x(x*)

拷貝建構函式並非要求只有乙個形參:如果乙個建構函式是自身型別的引用,且任何額外的引數都有預設實參,則此建構函式是拷貝建構函式。

例:x(const x&, int x = 10); //第二個引數有預設值

只需要提供乙個本型別物件作實參就可以呼叫的建構函式是拷貝建構函式。

那我們應何時呼叫拷貝建構函式

一、用乙個物件顯式或隱式初始化另乙個同類物件

二、函式呼叫時,按傳值方式傳遞物件引數

三、函式返回時,按傳值方式返回物件

以上三種情況都是用乙個物件初始化另乙個同類物件的語義,會呼叫拷貝建構函式。

按引用傳遞物件或返回物件時並不是建立和初始化物件的語義,因而不會引起拷貝建構函式的呼叫。

最後當我們使用完乙個物件時要對其進行銷毀,這時我們就要呼叫析構函式:

析構函式

析構函式包括函式體和隱式的析構部分,首先執行函式體,然後執行析構部分銷毀成員,成員按初始化的逆序銷毀,成員的銷毀方式完全依賴於成員的型別,銷毀類型別的成員需要執行成員自己的析構函式。

當我們初始化完畢之後,再對物件進行賦值操作複製所要得到的資料:

賦值要使用賦值運算子,下面對賦值運算子作一些介紹:

定義類時要控制物件如何賦值,可以過載賦值運算子,如果類沒有定義拷貝賦值運算子,編譯器會自動合成,行為是將右運算元物件的每個非static 成員賦值給左運算元物件的對應成員,最後返回左運算元物件的引用,類型別的成員通過其拷貝賦值運算子進行賦值,對陣列型別的成員,逐個陣列元素賦值.

定義方式:

類x 的賦值運算子要定義為類x 的成員函式:

x& operator=(const x&)

當進行x 類的物件賦值如a=b 時,就相當於呼叫成員函式a.operator=(b)

賦值運算子通常返回乙個指向左運算元的引用

x& operator=(const x&)

operator=()的基本行為是將右運算元中的資訊複製到左運算元中

例:

//乙個簡單的字串類

class my_string

~my_string()

my_string& operator=(const my_string& s);};…

my_string a("abcde"), b("hijk");

a = b; //如何賦值?

//第一種方式:淺複製,對應的成員之間直接賦值

//第二種方式:深複製,複製指標指向的單元

如果類中沒有過載operator=(),編譯器將在需要時自動合成乙個,其行為是按成員賦值

對於複雜的類,尤其是包含指標成員時,應該顯式地建立operator=()

賦值運算的方式

賦值運算的常規模式:

首先進行自賦值檢測:檢查左右運算元兩個物件的位址是否相等,如果相等,則它們是同一物件,如果是同一物件的賦值,一般直接返回當前物件。

然後執行正常的賦值操作序列:注意淺複製和深複製的區別。

最後返回當前物件:return *this;

賦值運算的另一種模式:

一、先將右運算元物件複製到乙個區域性臨時物件中,複製可能會呼叫拷貝建構函式。

二、銷毀左運算元物件的現有成員,複製完成後,銷毀左運算元物件的現有成員就安全了。

三、將資料從臨時物件複製到左運算元物件的成員中,賦值操作序列。

四.返回左運算元物件的引用,函式返回時,臨時物件被析構函式銷毀。

例:

my_string& my_string::operator=(const my_string& s)

C 直接初始化和複製初始化

在c 裡,物件初始化是乙個非常重要但又容易令人混淆的問題。這裡是自己的一些總結。一。初始化與賦值的含義 初始化 包括建立 或說定義 物件並且賦給初值。如果乙個物件只被建立而沒有被初始化,則該變數只能用於被賦值 賦值 擦除物件的當前值並用新值代替。二。內建型別 一 直接初始化 1.空初始化 即無引數無...

直接初始化和複製初始化

關於這個問題,國內外都有許多爭論,但我至今未找到滿意的答案,至於為為什麼,這就是今天要說明的。可能大家都有看過c primer,我看的是第四版,中文版,英文水平不怎麼樣。其中第13章,描述說,複製建構函式可用於 1.根據另乙個同型別的物件顯式或隱式初始化乙個物件 2.複製乙個物件,將它作為引數傳遞給...

直接初始化和複製初始化

1 classtest ct1 ab 這條語句屬於直接初始化,它不需要呼叫複製建構函式,直接呼叫建構函式classtest const char pc 所以當複製建構函式變為私有時,它還是能直接執行的。2 classtest ct2 ab 這條語句為複製初始化,它首先呼叫建構函式classtest ...