C++類特殊成員總結(jié)(一)

c++類的特殊成員是一個(gè)很重要的知識(shí)點(diǎn)。在平時(shí)寫代碼的過(guò)程中,我們都會(huì)使用到至少兩種以上的特殊成員(可能我們自己都沒(méi)有意識(shí)到自己在使用他們),但是錯(cuò)誤的使用他們會(huì)給我們的程序引入一些難以被發(fā)現(xiàn)的bug;在很多公司的筆試中也會(huì)涉及到,一旦和繼承等知識(shí)組合在了一起,就會(huì)變得非常燒腦和難以理解。因此筆者記下了這篇文章以及自己的一些實(shí)踐經(jīng)驗(yàn),來(lái)總結(jié)下C++類的特殊成員。

什么是C++類的特殊成員

Special member functions are member functions that are implicitly defined as member of classes under certain circumstances. There are six:


6種特殊成員

所謂特殊成員函數(shù)即上述六種被隱式定義作為類的成員的函數(shù)。

前兩種——默認(rèn)構(gòu)造函數(shù)和析構(gòu)函數(shù)都是我們經(jīng)常接觸的。


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


如上圖所示,我們?cè)趘s2013中輸入class關(guān)鍵字試圖創(chuàng)建一個(gè)自己的類時(shí),ide自動(dòng)補(bǔ)全后,就會(huì)自動(dòng)生成默認(rèn)構(gòu)造函數(shù)和析構(gòu)函數(shù),可見(jiàn)這是兩個(gè)最基本的函數(shù)。

現(xiàn)在來(lái)一一總結(jié)一番


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

The default constructor is the constructor called when objects of a class are declared, but are not initialized with any arguments.

我們將這兩個(gè)函數(shù)從定義中刪去,然后聲明一個(gè)MyClass 對(duì)象

發(fā)現(xiàn)運(yùn)行正常,也就是說(shuō)編譯器幫我們實(shí)現(xiàn)了一個(gè)默認(rèn)的構(gòu)造函數(shù)(但是我們不能對(duì)它執(zhí)行任何的操作,因?yàn)樗鼪](méi)有被初始化過(guò))

一旦我們給MyClass加上了含參數(shù)的構(gòu)造函數(shù),那么我們之前的聲明就無(wú)法通過(guò)編譯,因?yàn)榫幾g器不再提供一個(gè)默認(rèn)實(shí)現(xiàn)的構(gòu)造函數(shù)了。

when any constructor is explicitly declared in a class, no implicit default constructors is automatically provided.

現(xiàn)在給我們的MyClass加上默認(rèn)構(gòu)造函數(shù),錯(cuò)誤消失了。

我們發(fā)現(xiàn)value的值被設(shè)為了 -858993460



如果我們將我們的value的類型改為我們自定義的另一個(gè)類MyClassB時(shí),并運(yùn)行我們的代碼的時(shí)候,錯(cuò)誤再一次出現(xiàn)了


也就是說(shuō)當(dāng)我們沒(méi)有指定默認(rèn)構(gòu)造函數(shù)如何定義類的成員變量的時(shí)候,編譯器會(huì)自動(dòng)調(diào)用該成員變量的構(gòu)造函數(shù)。

上述這些內(nèi)容,我們都可以在c++官網(wǎng)上找到相應(yīng)的依據(jù),但是如果我們把繼承相關(guān)的知識(shí)也一起結(jié)合起來(lái),又出現(xiàn)了新的問(wèn)題——當(dāng)B繼承A的時(shí)候,B的構(gòu)造函數(shù)是不是也被一起繼承了呢?

同樣的我們還是先嘗試從官網(wǎng)上尋找答案(非常推薦遇到問(wèn)題首先去官方尋找答案)


What is inherited from the base class?

根據(jù)官網(wǎng)上的說(shuō)法,子類不會(huì)繼承父類的默認(rèn)構(gòu)造函數(shù),但是我的實(shí)踐發(fā)現(xiàn),父類的默認(rèn)構(gòu)造函數(shù)依然還是被調(diào)用了。參加下圖

相應(yīng)地也在官網(wǎng)上找到了相應(yīng)的依據(jù)

Even though access to the constructors and destructor of the base class is not inherited as such, they are automatically called by the constructors and destructor of the derived class.

雖然構(gòu)造函數(shù)和析構(gòu)函數(shù)不會(huì)被繼承,但是依然會(huì)被子類的構(gòu)造函數(shù)依然會(huì)調(diào)用父類的構(gòu)造函數(shù)。那么問(wèn)題又來(lái)了,如果有多個(gè)構(gòu)造函數(shù),應(yīng)該會(huì)調(diào)用哪一個(gè)呢?


我們發(fā)現(xiàn),無(wú)論我們調(diào)用子類的哪個(gè)構(gòu)造函數(shù),均會(huì)調(diào)用基類的默認(rèn)構(gòu)造函數(shù)。來(lái)看看官網(wǎng)的解釋:

Unless otherwise specified, the constructors of a derived class calls the default constructor of its base classes (i.e., the constructor taking no arguments).

那么我們能不能使我們的子類調(diào)用我們指定的基類的構(gòu)造函數(shù)呢?答案當(dāng)然是肯定的。


加上上圖中紅色橢圓圈出來(lái)的代碼即可指定調(diào)用的基類的構(gòu)造函數(shù)了,觀察我們的輸出,我們已經(jīng)達(dá)到了預(yù)期的效果。


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

析構(gòu)函數(shù)是一個(gè)相對(duì)陌生的概念,因?yàn)楫?dāng)我們創(chuàng)建一個(gè)類的時(shí)候,我們并不見(jiàn)得一定會(huì)動(dòng)態(tài)分配內(nèi)存(如果不知道動(dòng)態(tài)分配內(nèi)存,你需要先找些資料了解什么是動(dòng)態(tài)內(nèi)存分配)。析構(gòu)函數(shù)就是負(fù)責(zé)在對(duì)象被銷毀后,負(fù)責(zé)幫助我們回收這些內(nèi)存的成員,它在對(duì)象生命周期到達(dá)盡頭的時(shí)候被調(diào)用。

下面是來(lái)自官網(wǎng)的定義

Destructors fulfill the opposite functionality of constructors: They are responsible for the necessary cleanup needed by a class when its lifetime ends.

同樣的,我們也寫了代碼來(lái)嘗試一下,析構(gòu)函數(shù)


很多同學(xué)會(huì)好奇,c++類的析構(gòu)函數(shù)除了我們回收我們分配的內(nèi)存(這聽(tīng)起來(lái)有些抽象),具體有些什么應(yīng)用場(chǎng)景呢?

《effective c++》中的條款13中提出:

Use objects to manage resources.

這個(gè)意思就是說(shuō),我們要把我們的對(duì)象,作為管理資源的容器。那么什么是資源呢?上文中提到的動(dòng)態(tài)分配的內(nèi)存是資源,一個(gè)文件描述符是資源,數(shù)據(jù)庫(kù)的一個(gè)連接是資源,一個(gè)tensorflow的會(huì)話也是資源......在書中也給出了概括性的資源的概念

所謂資源就是,一旦用了它,將來(lái)必須還給系統(tǒng)。如果不這樣,糟糕的事情就會(huì)發(fā)生。

什么樣的糟糕的事呢?死鎖、內(nèi)存泄漏,都會(huì)因?yàn)槲覀兺洑w還資源而發(fā)生。

看我們下面這段代碼,resource是我們申請(qǐng)的資源,我們對(duì)它進(jìn)行一系列的操作后將它歸還給系統(tǒng)。


這段代碼有什么問(wèn)題嗎?顯然沒(méi)什么問(wèn)題。但是一旦我們用注釋表示的“一系列的操作”中發(fā)生了意外,那么,resource就會(huì)因?yàn)楹瘮?shù)過(guò)早的return而不會(huì)被執(zhí)行。比如下面這段代碼。

一旦我們輸入為0,resource就永遠(yuǎn)不會(huì)還給系統(tǒng)

當(dāng)然,你也可以在外面加上一個(gè)try...catch語(yǔ)句塊,在發(fā)生錯(cuò)誤后,依然保證釋放資源。但是沒(méi)有人能夠保證能夠發(fā)現(xiàn)代碼中所有的錯(cuò)誤,并且在處理異常的同時(shí)歸還資源。

正確的做法正是利用c++的析構(gòu)函數(shù)的自動(dòng)調(diào)用的機(jī)制——對(duì)象生命周期結(jié)束的時(shí)候析構(gòu)函數(shù)會(huì)自動(dòng)被調(diào)用,將資源放入對(duì)象中,在析構(gòu)函數(shù)中釋放資源。

下圖中的代碼體現(xiàn)了這種思想。

c++的智能指針也是用同樣的思路實(shí)現(xiàn)的。各位同學(xué)如果面試時(shí)候被問(wèn)到,要回憶下這篇文章的內(nèi)容。

PS:《effective C++》是本好書,這里安利下。

參考文獻(xiàn):c++官網(wǎng)

《effective c++》


下一篇文章,會(huì)總結(jié)下copy constructor和copy assignment

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

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

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