C++類和對(duì)象之默認(rèn)成員函數(shù)的使用解讀
一、默認(rèn)成員函數(shù)有哪些
C++ 類的默認(rèn)成員函數(shù)主要有六個(gè),分別是默認(rèn)構(gòu)造函數(shù)、析構(gòu)函數(shù)、拷貝構(gòu)造函數(shù)、拷貝賦值運(yùn)算符和移動(dòng)構(gòu)造函數(shù)、移動(dòng)賦值運(yùn)算符(C++11 引入)。
它們各自承擔(dān)著不同的職責(zé),在對(duì)象的生命周期中發(fā)揮著關(guān)鍵作用。
二、各默認(rèn)成員函數(shù)詳解
默認(rèn)構(gòu)造函數(shù)
默認(rèn)構(gòu)造函數(shù)是用于創(chuàng)建對(duì)象時(shí)初始化對(duì)象的特殊成員函數(shù)。它不需要任何參數(shù),如果我們沒(méi)有在類中顯式定義構(gòu)造函數(shù),編譯器會(huì)自動(dòng)生成一個(gè)默認(rèn)構(gòu)造函數(shù)。
構(gòu)造函數(shù)的存在完美的代替了Init函數(shù)的作用,并且由于他是自動(dòng)調(diào)用,也方便使用了許多。
例如:
class MyClass { // 編譯器會(huì)生成默認(rèn)構(gòu)造函數(shù) int data; };
在上述代碼中,MyClass類沒(méi)有顯式定義構(gòu)造函數(shù),編譯器會(huì)生成一個(gè)默認(rèn)構(gòu)造函數(shù)。當(dāng)使用MyClass obj;這樣的語(yǔ)句創(chuàng)建對(duì)象obj時(shí),默認(rèn)構(gòu)造函數(shù)就會(huì)被調(diào)用。
不過(guò)需要注意的是,編譯器生成的默認(rèn)構(gòu)造函數(shù)只是對(duì)對(duì)象的成員進(jìn)行默認(rèn)初始化,對(duì)于內(nèi)置類型,其值是未定義的;對(duì)于類類型成員,則會(huì)調(diào)用該成員類的默認(rèn)構(gòu)造函數(shù)進(jìn)行初始化。
在類成員為自定義成員時(shí),由于編譯器所構(gòu)造的函數(shù)不能夠完美滿足使用者的需求,所以大多數(shù)情況下要求用于自行去實(shí)現(xiàn)一個(gè)構(gòu)造函數(shù)。
構(gòu)造函數(shù)的特點(diǎn):
- 函數(shù)名與類名相同。
- ?返回值,也不需要void。
- 對(duì)象實(shí)例化時(shí)系統(tǒng)會(huì)?動(dòng)調(diào)?對(duì)應(yīng)的構(gòu)造函數(shù)。
- 構(gòu)造函數(shù)可以重載。
- 如果類中沒(méi)有顯式定義構(gòu)造函數(shù),則C++編譯器會(huì)?動(dòng)?成?個(gè)?參的默認(rèn)構(gòu)造函數(shù),?旦??顯式定義編譯器將不再?成。
- ?參構(gòu)造函數(shù)、全缺省構(gòu)造函數(shù)、我們不寫構(gòu)造時(shí)編譯器默認(rèn)?成的構(gòu)造函數(shù),都叫做默認(rèn)構(gòu)造函數(shù)。但是這三個(gè)函數(shù)有且只有?個(gè)存在,不能同時(shí)存在。?參構(gòu)造函數(shù)和全缺省構(gòu)造函數(shù)雖然構(gòu)成函數(shù)重載,但是調(diào)?時(shí)會(huì)存在歧義。要注意很多同學(xué)會(huì)認(rèn)為默認(rèn)構(gòu)造函數(shù)是編譯器默認(rèn)?成那個(gè)叫默認(rèn)構(gòu)造,實(shí)際上?參構(gòu)造函數(shù)、全缺省構(gòu)造函數(shù)也是默認(rèn)構(gòu)造,總結(jié)?下就是不傳實(shí)參就可以調(diào)?的構(gòu)造就叫默認(rèn)構(gòu)造。
- 我們不寫,編譯器默認(rèn)?成的構(gòu)造,對(duì)內(nèi)置類型成員變量的初始化沒(méi)有要求,也就是說(shuō)是是否初始化是不確定的,看編譯器。對(duì)于?定義類型成員變量,要求調(diào)?這個(gè)成員變量的默認(rèn)構(gòu)造函數(shù)初始化。如果這個(gè)成員變量,沒(méi)有默認(rèn)構(gòu)造函數(shù),那么就會(huì)報(bào)錯(cuò),我們要初始化這個(gè)成員變量,需要?初始化列表才能解決。
析構(gòu)函數(shù)
析構(gòu)函數(shù)與構(gòu)造函數(shù)相對(duì),用于在對(duì)象生命周期結(jié)束時(shí)進(jìn)行清理工作,釋放對(duì)象所占用的資源。析構(gòu)函數(shù)沒(méi)有返回值,也不需要參數(shù),并且名稱與類名相同,只是前面加上一個(gè)波浪號(hào)~。和默認(rèn)構(gòu)造函數(shù)類似,如果我們沒(méi)有顯式定義析構(gòu)函數(shù),編譯器會(huì)生成一個(gè)默認(rèn)析構(gòu)函數(shù)。
析構(gòu)函數(shù)主要代替的是destroy函數(shù)的作用,在類結(jié)束時(shí)自動(dòng)調(diào)用銷毀。析構(gòu)函數(shù)主要處理的是經(jīng)過(guò)動(dòng)態(tài)內(nèi)存申請(qǐng)的類。
class MyClass { // 編譯器會(huì)生成默認(rèn)析構(gòu)函數(shù) int* data; };
對(duì)于大多數(shù)簡(jiǎn)單類,默認(rèn)析構(gòu)函數(shù)就足夠了,它會(huì)自動(dòng)調(diào)用成員對(duì)象的析構(gòu)函數(shù),釋放對(duì)象成員占用的資源。但如果類中涉及到動(dòng)態(tài)內(nèi)存分配,就需要顯式定義析構(gòu)函數(shù)來(lái)釋放這些動(dòng)態(tài)分配的資源,否則會(huì)導(dǎo)致內(nèi)存泄漏。例如:
class MyClass { public: MyClass() { data = new int; } ~MyClass() { delete data; // 顯式釋放動(dòng)態(tài)分配的內(nèi)存 } private: int* data; };
析構(gòu)函數(shù)的特點(diǎn):
- 析構(gòu)函數(shù)名是在類名前加上字符~。
- ?參數(shù)?返回值,則無(wú)void。
- ?個(gè)類只能有?個(gè)析構(gòu)函數(shù)。若未顯式定義,系統(tǒng)會(huì)?動(dòng)?成默認(rèn)的析構(gòu)函數(shù)。
- 對(duì)象?命周期結(jié)束時(shí),系統(tǒng)會(huì)?動(dòng)調(diào)?析構(gòu)函數(shù)。
- 跟構(gòu)造函數(shù)類似,我們不寫編譯器?動(dòng)?成的析構(gòu)函數(shù)對(duì)內(nèi)置類型成員不做處理,?定類型成員會(huì)調(diào)?他的析構(gòu)函數(shù)。
- 還需要注意的是我們顯?寫析構(gòu)函數(shù),對(duì)于?定義類型成員也會(huì)調(diào)?他的析構(gòu),也就是說(shuō)?定義類型成員?論什么情況都會(huì)?動(dòng)調(diào)?析構(gòu)函數(shù)。
- 如果類中沒(méi)有申請(qǐng)資源時(shí),析構(gòu)函數(shù)可以不寫,直接使?編譯器?成的默認(rèn)析構(gòu)函數(shù);如果默認(rèn)?成的析構(gòu)就可以?,也就不需要顯?寫析構(gòu);但是有資源申請(qǐng)時(shí),?定要??寫析構(gòu),否則會(huì)造成資源泄漏。
拷貝構(gòu)造函數(shù)
拷貝構(gòu)造函數(shù)用于用一個(gè)已存在的對(duì)象創(chuàng)建一個(gè)新的對(duì)象,本質(zhì)上是進(jìn)行對(duì)象的復(fù)制操作。它的參數(shù)是本類對(duì)象的常量引用,例如:
class MyClass { public: MyClass(int value) : data(value) {} MyClass(const MyClass& other) : data(other.data) {} // 自定義拷貝構(gòu)造函數(shù) private: int data; };
如果我們沒(méi)有顯式定義拷貝構(gòu)造函數(shù),編譯器會(huì)生成一個(gè)默認(rèn)的拷貝構(gòu)造函數(shù)。默認(rèn)拷貝構(gòu)造函數(shù)會(huì)對(duì)對(duì)象的每個(gè)成員進(jìn)行逐成員拷貝,對(duì)于基本數(shù)據(jù)類型,這種拷貝方式?jīng)]有問(wèn)題;但對(duì)于指針類型成員,默認(rèn)拷貝構(gòu)造函數(shù)只是簡(jiǎn)單地復(fù)制指針的值,這可能會(huì)導(dǎo)致兩個(gè)對(duì)象的指針指向同一塊內(nèi)存,從而引發(fā)內(nèi)存管理問(wèn)題。
拷貝賦值運(yùn)算符
如果?個(gè)構(gòu)造函數(shù)的第?個(gè)參數(shù)是??類類型的引?,且任何額外的參數(shù)都有默認(rèn)值,則此構(gòu)造函數(shù)也叫做拷?構(gòu)造函數(shù),也就是說(shuō)拷?構(gòu)造是?個(gè)特殊的構(gòu)造函數(shù)。
拷貝賦值運(yùn)算符用于將一個(gè)已存在的對(duì)象賦值給另一個(gè)同類型的對(duì)象,它的函數(shù)形式為類名& operator=(const 類名& other) 。同樣,如果沒(méi)有顯式定義,編譯器會(huì)生成默認(rèn)的拷貝賦值運(yùn)算符。
class MyClass { public: MyClass& operator=(const MyClass& other) { if (this!= &other) { data = other.data; } return *this; } private: int data; };
默認(rèn)的拷貝賦值運(yùn)算符也會(huì)進(jìn)行逐成員賦值,和默認(rèn)拷貝構(gòu)造函數(shù)一樣,在處理指針類型成員時(shí)可能會(huì)出現(xiàn)問(wèn)題。在實(shí)際編程中,當(dāng)類涉及到資源管理時(shí),我們需要自定義拷貝賦值運(yùn)算符,確保資源的正確處理。
拷?構(gòu)造的特點(diǎn):
- 拷?構(gòu)造函數(shù)是構(gòu)造函數(shù)的?個(gè)重載。
- 拷?構(gòu)造函數(shù)的第?個(gè)參數(shù)必須是類類型對(duì)象的引?,使?傳值?式編譯器直接報(bào)錯(cuò),因?yàn)檎Z(yǔ)法邏輯上會(huì)引發(fā)?窮遞歸調(diào)?。拷?構(gòu)造函數(shù)也可以多個(gè)參數(shù),但是第?個(gè)參數(shù)必須是類類型對(duì)象的引?,后?的參數(shù)必須有缺省值。因?yàn)樾螀?huì)創(chuàng)建一個(gè)臨時(shí)變量,這個(gè)臨時(shí)變量又會(huì)進(jìn)行拷貝
- C++規(guī)定?定義類型對(duì)象進(jìn)?拷??為必須調(diào)?拷?構(gòu)造,所以這??定義類型傳值傳參和傳值返回都會(huì)調(diào)?拷?構(gòu)造完成。
- 若未顯式定義拷?構(gòu)造,編譯器會(huì)?成?動(dòng)?成拷?構(gòu)造函數(shù)。?動(dòng)?成的拷?構(gòu)造對(duì)內(nèi)置類型成員變量會(huì)完成值拷?/淺拷?(?個(gè)字節(jié)?個(gè)字節(jié)的拷?),對(duì)?定義類型成員變量會(huì)調(diào)?他的拷?構(gòu)造。
- 傳值返回會(huì)產(chǎn)??個(gè)臨時(shí)對(duì)象調(diào)?拷?構(gòu)造,傳值引?返回,返回的是返回對(duì)象的別名(引?),沒(méi)有產(chǎn)?拷?。但是如果返回對(duì)象是?個(gè)當(dāng)前函數(shù)局部域的局部對(duì)象,函數(shù)結(jié)束就銷毀了,那么使?引?返回是有問(wèn)題的,這時(shí)的引?相當(dāng)于?個(gè)野引?,類似?個(gè)野指針?樣。傳引?返回可以減少拷?,但是?定要確保返回對(duì)象,在當(dāng)前函數(shù)結(jié)束后還在,才能?引?返回。
- 若是未進(jìn)行動(dòng)態(tài)內(nèi)存申請(qǐng)的數(shù)據(jù),則淺拷貝也能可以;但若進(jìn)行了動(dòng)態(tài)內(nèi)存分配,則需要自己實(shí)現(xiàn)一個(gè)拷貝構(gòu)造,來(lái)實(shí)現(xiàn)深拷貝。這是因?yàn)閯?dòng)態(tài)內(nèi)存分配的空間,淺拷貝時(shí)會(huì)指向同一塊空間,會(huì)析構(gòu)兩次導(dǎo)致程序崩潰。
三、默認(rèn)成員函數(shù)的注意事項(xiàng)
注意事項(xiàng):
- 在使用默認(rèn)成員函數(shù)時(shí),要特別注意類中成員的類型。
- 如果類包含指針類型成員或其他需要特殊資源管理的成員,一定要謹(jǐn)慎使用默認(rèn)的拷貝構(gòu)造函數(shù)、拷貝賦值運(yùn)算符等,避免出現(xiàn)內(nèi)存泄漏或懸空指針等問(wèn)題。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
淺析string類字符串和C風(fēng)格字符串之間的區(qū)別
string類是標(biāo)準(zhǔn)庫(kù)的類,并不是內(nèi)置類型,標(biāo)準(zhǔn)庫(kù)就像是我們自己定義的類差不多的,string類型對(duì)象沒(méi)有標(biāo)配'\0'結(jié)尾的2013-09-09C++ Boost Coroutine使用協(xié)程詳解
通過(guò)Boost.Coroutine,可以在C++中使用協(xié)程。協(xié)程是其他編程語(yǔ)言的一個(gè)特性,通常使用關(guān)鍵字yield來(lái)表示協(xié)程。在這些編程語(yǔ)言中,yield可以像return一樣使用2022-11-11C語(yǔ)言課程設(shè)計(jì)之抽獎(jiǎng)系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言課程設(shè)計(jì)之抽獎(jiǎng)系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12c++ class中成員與分配內(nèi)存的問(wèn)題詳解
很多人都知道C++類是由結(jié)構(gòu)體發(fā)展得來(lái)的,所以他們的成員變量(C語(yǔ)言的結(jié)構(gòu)體只有成員變量)的內(nèi)存分配機(jī)制是一樣的,下面這篇文章主要給大家介紹了關(guān)于c++ class中成員與分配內(nèi)存問(wèn)題的相關(guān)資料,需要的朋友可以參考下2021-10-10C語(yǔ)言輸入三角形邊長(zhǎng)判斷其類型并輸出面積實(shí)例代碼
這篇文章主要介紹了C語(yǔ)言輸入三角形邊長(zhǎng)判斷其類型并輸出面積實(shí)例代碼,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01C++數(shù)據(jù)結(jié)構(gòu)之哈希表的實(shí)現(xiàn)
哈希表,即散列表,可以快速地存儲(chǔ)和查詢記錄。這篇文章主要為大家詳細(xì)介紹了C++數(shù)據(jù)結(jié)構(gòu)中哈希表的實(shí)現(xiàn),感興趣的小伙伴可以了解一下2023-03-03C++算法計(jì)時(shí)器的實(shí)現(xiàn)示例
本文主要介紹了C++算法計(jì)時(shí)器的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05