C++11六大函數(shù)(構(gòu)造函數(shù),移動構(gòu)造函數(shù),移動賦值操作符,復(fù)制構(gòu)造函數(shù),賦值操作符,析構(gòu)函數(shù))

在C++中,有三大函數(shù)復(fù)制控制(復(fù)制構(gòu)造函數(shù),賦值操作符,析構(gòu)函數(shù)),而在C++11中,加入了移動構(gòu)造函數(shù),移動賦值操作符。我就斗膽將他們命名為六大函數(shù)好了。

一、構(gòu)造函數(shù)

C++ Primer中說過:構(gòu)造函數(shù)是特殊的成員函數(shù),只要創(chuàng)建類類型的新對象,都要執(zhí)行構(gòu)造函數(shù)。構(gòu)造函數(shù)的工作就是保證每個對象的數(shù)據(jù)成員具有合適的初始值。

構(gòu)造函數(shù)與其他函數(shù)不同:構(gòu)造函數(shù)和類同名,沒有返回類型。

構(gòu)造函數(shù)與其他函數(shù)相同:構(gòu)造函數(shù)也有形參表(可為void)和函數(shù)體。 ?(參數(shù)表為void的構(gòu)造函數(shù)為默認(rèn)構(gòu)造函數(shù))

構(gòu)造函數(shù)構(gòu)造類對象的順序是:1.內(nèi)存分配,構(gòu)造函數(shù)調(diào)用的時候 隱士\顯示的初始化各數(shù)據(jù)。

2.執(zhí)行構(gòu)造函數(shù)的運行。

1、構(gòu)造函數(shù)初始化表

A() :a(0){}

我們使用構(gòu)造函數(shù)初始化表示初始化數(shù)據(jù)成員,然而在沒有使用初始化表的構(gòu)造函數(shù)則在構(gòu)造函數(shù)體中對數(shù)據(jù)成員賦值。

在我們編寫類的時候,有些成員必須在構(gòu)造函數(shù)初始化表中進(jìn)行初始化。(沒有默認(rèn)構(gòu)造函數(shù)的類類型成員,const或者引用類型成員)

在編寫代碼的時候,要注意的是:可以初始化const對象或者引用類型的對象,但不能對他們進(jìn)行賦值。 也就是需要在我們執(zhí)行構(gòu)造函數(shù)函數(shù)體之前完成初始化工作,所以唯一的機(jī)會就是初始化表。從這一點可以看出初始化表的執(zhí)行先于函數(shù)體。

在初始化表中,成員被初始化的次序不是你編寫初始化表的次序,而是定義成員的次序。

初始化列表在初始化類類型的成員時,要指定實參并傳遞給成員類型的一個構(gòu)造函數(shù)。

在c++primer中有一個書店的例子:

Sales-item(): isbn(10,'9'), units_sold(0), revenue(0.0) {}

我們的初始化表在什么時候必須使用呢 ?

當(dāng)有一個類成員,他本身就是結(jié)構(gòu)或者類的時候,并且只有一個帶參數(shù)的構(gòu)造函數(shù),(無默認(rèn)構(gòu)造函數(shù)) 此時我們要對成員進(jìn)行初始化,就需要調(diào)用成員的構(gòu)造函數(shù),此時需要我們的初始化表,如果不使用初始化表,那么內(nèi)存分配就會出問題。

初始化列表的優(yōu)點:主要是對于自定義類型,初始化列表是作用在函數(shù)體之前,他調(diào)用構(gòu)造函數(shù)對對象進(jìn)行初始化。

然而在函數(shù)體內(nèi),需要先調(diào)用構(gòu)造函數(shù),然后進(jìn)行賦值,這樣效率就不如初始化表。

2、默認(rèn)實參構(gòu)造函數(shù)

A(inti = 1) :a(i), ca(i), ra(i){}

3、默認(rèn)構(gòu)造函數(shù)

合成的默認(rèn)構(gòu)造函數(shù):當(dāng)類中沒有定義構(gòu)造函數(shù)(注意是構(gòu)造函數(shù))的時候,編譯器自動生成的函數(shù)。

但是我們不能過分依賴編譯器,如果我們的類中有復(fù)合類型或者自定義類型成員,我們需要自己定義構(gòu)造函數(shù)。

自定義的默認(rèn)構(gòu)造函數(shù):

A(): a(0) {}

A(inti?=?1):?a(i)?{}

可能疑問的是第二個構(gòu)造函數(shù)也是默認(rèn)構(gòu)造函數(shù)么?是的,因為參數(shù)中帶有默認(rèn)值。

4、類型轉(zhuǎn)換

在C++primer中,書店問題中的一個例子是 傳遞string對象或者iostream對象到參數(shù)中,會發(fā)生隱式轉(zhuǎn)換,這樣就會出現(xiàn)問題。

explicit關(guān)鍵字可以抑制隱式轉(zhuǎn)換。

explicitSales_item(conststring &book): isbn(book) {}

如果我們聲明了構(gòu)造函數(shù)禁止隱式轉(zhuǎn)換, 可以將其他對象顯示轉(zhuǎn)換后傳入構(gòu)造函數(shù)。

string a ="d";

item.same(Sales_item(a));

二、移動構(gòu)造函數(shù)

在C++11中新加入的特性!

通過下圖,可以具體看到移動構(gòu)造函數(shù)的運行原理。

此時,我們偷走了臨時變量的內(nèi)存空間,據(jù)為己用。節(jié)省了開辟空間的時間。

A(A && h) : a(h.a) {

h.a?=?nullptr;//還記得nullptr?

}

可以看到,這個構(gòu)造函數(shù)的參數(shù)不同,有兩個&操作符,? 移動構(gòu)造函數(shù)接收的是“右值引用”的參數(shù)。

還要來說一下,這里h.a置為空,如果不這樣做,h.a在移動構(gòu)造函數(shù)結(jié)束時候執(zhí)行析構(gòu)函數(shù)會將我們偷來的內(nèi)存析構(gòu)掉。h.a會變成懸垂指針。

移動構(gòu)造函數(shù)何時觸發(fā)? ?那就是臨時對象(右值)。用到臨時對象的時候就會執(zhí)行移動語義。

這里要注意的是,異常發(fā)生的情況,要盡量保證移動構(gòu)造函數(shù) 不發(fā)生異常,可以通過noexcept關(guān)鍵字,這里可以保證移動構(gòu)造函數(shù)中拋出來的異常會直接調(diào)用terminate終止程序。

右值引用:

在上一篇blog中,我們提到過將亡值,他是c++11新增的跟右值引用相關(guān)的表達(dá)式。

在c++11中,右值引用就是對一個右值進(jìn)行引用的類型,右值通常不具有名字,我們就只能通過引用的方式找到它的存在了。

比較一下下面兩條語句:

T &&a = returna();

T?b?=?returnb();

此時a是右值引用,他比b少了一次對象析構(gòu)和對象構(gòu)造的過程。a直接綁定了returna返回的臨時變量。b只是由臨時變量值構(gòu)造而成的。

應(yīng)該可以看清楚了吧。右值引用就是讓返回的右值(臨時對象)重獲新生,延長生命周期。臨時對象析構(gòu)了,但是右值引用存活。

不過要注意的是,右值引用不能綁定左值:int a; int &&c = a; ? 這樣是不行的。

這里有一個函數(shù)就是 move函數(shù),它能夠?qū)⒆笾祻?qiáng)制轉(zhuǎn)換成右值引用。

三、移動賦值操作符

他的原理跟移動

構(gòu)造函數(shù)相同,這里不再多說。

給出實現(xiàn)代碼:

A & operator = (A&& h)

{

assert(this!=?&h);

a?=?nullptr;

a?=?move(h.a);

h.a?=?nullptr;

return*this;

}

復(fù)制控制

四、復(fù)制構(gòu)造函數(shù)

他是一種特殊的構(gòu)造函數(shù),具有單個形參,形參是對該類類型的引用。當(dāng)定義一個新對象并用一個同類型的對象對它進(jìn)行初始化時,將顯式使用復(fù)制構(gòu)造函數(shù)。當(dāng)將該類型的對象傳遞給函數(shù)或從函數(shù)返回該類型的對象時,將隱式使用復(fù)制構(gòu)造函數(shù)。

必須定義復(fù)制構(gòu)造函數(shù)的情況:

1.、類有一個或者多個數(shù)據(jù)成員是指針。

2、有成員表示在構(gòu)造函數(shù)中分配的其他資源。另外的類在創(chuàng)建新對象時必須做一些特定的工作。

下面給出賦值構(gòu)造函數(shù)的編寫:

A(constA& h) :a(h.a){}

如果不想讓對象復(fù)制呢? 那就將復(fù)制構(gòu)造函數(shù)聲明為:private;

五、賦值操作符

他跟構(gòu)造函數(shù)一樣,賦值操作符可以通過制定不同類型的右操作數(shù)而重載。

賦值和復(fù)制經(jīng)常是一起使用的,這個要注意。

下面給出賦值操作符的寫法:

五、賦值操作符

他跟構(gòu)造函數(shù)一樣,賦值操作符可以通過制定不同類型的右操作數(shù)而重載。

賦值和復(fù)制經(jīng)常是一起使用的,這個要注意。

下面給出賦值操作符的寫法:

A& operator = (constA& h)

{

assert(this!=?&h);

this->a?=?h.a;

return*this;

}

六、析構(gòu)函數(shù)

是構(gòu)造函數(shù)的互補(bǔ),當(dāng)對象超出作用域或動態(tài)分配的對象被刪除時,將自動應(yīng)用析構(gòu)函數(shù)。析構(gòu)函數(shù)可用于釋放對象時構(gòu)造或在對象的生命期中所獲取的資源。不管類是否定義了自己的析構(gòu)函數(shù),編譯器都會自動執(zhí)行類中非static數(shù)據(jù)成員的析構(gòu)函數(shù)。

析構(gòu)函數(shù)的運行:

當(dāng)對象引用或指針越界的時候不會執(zhí)行析構(gòu)函數(shù),只有在刪除指向動態(tài)分配對象的指針或?qū)嶋H對象超出作用域時才會調(diào)用析構(gòu)函數(shù)。

合成析構(gòu)函數(shù):

編譯器總是會合成一個析構(gòu)函數(shù),合成析構(gòu)函數(shù)按對象創(chuàng)建時的逆序撤銷每個非static成員。要注意的是,合成的析構(gòu)函數(shù)不會刪除指針成員所指向的對象。

最后要注意的是:類如果需要析構(gòu)函數(shù),那么他肯定也需要復(fù)制構(gòu)造函數(shù)和賦值操作符。

blog的最后給出完整的六大函數(shù)的代碼。

本文詳情于本人舊博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容