正確理解C++的構(gòu)造函數(shù)和析構(gòu)函數(shù)
一、構(gòu)造函數(shù)
首先,由于類(lèi)只是一個(gè)模板,因此我們?cè)诙x類(lèi)時(shí)無(wú)法對(duì)成員變量初始化,比如下面代碼就是錯(cuò)誤的:
class circle{
public:
int m_L = 20; // Error:不允許使用數(shù)據(jù)成員初始值設(shè)定項(xiàng)
};
因此,初始化只能發(fā)生在類(lèi)創(chuàng)建對(duì)象的過(guò)程中,但是由于訪問(wèn)權(quán)限的原因,無(wú)法在類(lèi)外訪問(wèn)某些成員變量,因此下面這種做法有時(shí)候是無(wú)效的:
circle C1; // 實(shí)例化一個(gè)對(duì)象 C1 C1.m_L = 20; // 通過(guò)創(chuàng)建的對(duì)象,來(lái)給對(duì)應(yīng)變量初始化,但是如果m_L是private訪問(wèn)權(quán)限,則失效
為了解決這個(gè)問(wèn)題,讓程序員能像使用標(biāo)準(zhǔn)數(shù)據(jù)類(lèi)型一樣適用對(duì)象,在類(lèi)內(nèi)提供了一個(gè)特殊的成員函數(shù)——“構(gòu)造函數(shù)”,專(zhuān)門(mén)用于在創(chuàng)建對(duì)象時(shí)初始化類(lèi)對(duì)象。之所以說(shuō)它特殊,是因?yàn)镃++已經(jīng)自動(dòng)為構(gòu)造函數(shù)提供了名稱(chēng)和使用語(yǔ)法,程序員只需要提供方法的定義即可,即:類(lèi)名(形參列表)。具體來(lái)說(shuō),構(gòu)造函數(shù)的定義如下:
class circle{
public:
int m_L;
public: //成員函數(shù)(方法)
circle(const int a) //通過(guò)構(gòu)造函數(shù)對(duì)成員變量進(jìn)行賦值
{
m_L = a;
}
};
circle C1(20); //調(diào)用格式
cout << "C1.m_L:" << C1.m_L <<endl;
看上去似乎很簡(jiǎn)單,但是由于構(gòu)造函數(shù)也是函數(shù),因此所有C++中的形參傳遞方式,函數(shù)特性以及函數(shù)調(diào)用方法都能用于構(gòu)造函數(shù)。 如前文所講,C++會(huì)自動(dòng)給類(lèi)添加一個(gè)空的構(gòu)造函數(shù),但是如果自己在類(lèi)中實(shí)現(xiàn)了有參構(gòu)造函數(shù),編譯器便不再提供無(wú)參構(gòu)造函數(shù)。舉例如下:
class circle{
public:
int m_L;
public: //成員函數(shù)(方法)
circle(const int a) //通過(guò)構(gòu)造函數(shù)對(duì)成員變量進(jìn)行賦值
{
m_L = a;
}
};
circle C1(20); //調(diào)用格式正確,能夠通過(guò)構(gòu)造函數(shù)賦值
circle C2; //錯(cuò)誤,自己定義了有參構(gòu)造函數(shù),不再提供無(wú)參構(gòu)造函數(shù)
構(gòu)造函數(shù)可以重載,接著上面的例子,如果重載一個(gè)空的構(gòu)造函數(shù),那么兩個(gè)調(diào)用格式都正確:
class circle{
public:
int m_L;
public: //成員函數(shù)(方法)
circle(){} // 空構(gòu)造函數(shù)
circle(const int a) //通過(guò)構(gòu)造函數(shù)對(duì)成員變量進(jìn)行賦值
{
m_L = a;
}
};
circle C1(20); //調(diào)用格式正確,能夠通過(guò)構(gòu)造函數(shù)賦值
circle C2; //正確,可以通過(guò)重載的空構(gòu)造函數(shù)實(shí)現(xiàn)初始化
構(gòu)造函數(shù)的參數(shù)不僅可以是標(biāo)準(zhǔn)數(shù)據(jù)類(lèi)型,也可以是類(lèi)。眾所周知,在數(shù)值作為函數(shù)參數(shù)進(jìn)行傳遞的時(shí)候,會(huì)重新拷貝出來(lái)一份數(shù)據(jù)作為參數(shù)傳遞用完即銷(xiāo)毀,這種方式不僅浪費(fèi)了內(nèi)存空間,而且無(wú)法修改原始數(shù)據(jù)。為了結(jié)合這兩者之間的優(yōu)點(diǎn),于是經(jīng)常采取引用作為函數(shù)的參數(shù)。雖然引用是指針的一種特殊情況,但是指針太過(guò)于靈活,并且引用在形式上引用與普通的變量地用法并沒(méi)有什么區(qū)別,因此使用起來(lái)更加方便。
class circle{
public:
int m_L;
public: //成員函數(shù)(方法)
circle(){} // 空構(gòu)造函數(shù)
circle(const int a) //通過(guò)構(gòu)造函數(shù)對(duì)成員變量進(jìn)行賦值
{
m_L = a;
}
//引用作為函數(shù)參數(shù)傳遞,并用const修飾,節(jié)省空間的同時(shí)避免修改原數(shù)據(jù)
circle(const circle& sub_circle)
{
m_L = sub_circle.m_L;
}
};
circle C1(20); //調(diào)用格式正確,能夠通過(guò)構(gòu)造函數(shù)賦值
circle C2(C1); //正確,可以通過(guò)拷貝構(gòu)造函數(shù)進(jìn)行初始化
二、C++類(lèi)的內(nèi)存模型
C++中,一個(gè)類(lèi)包括:
- 成員變量:靜態(tài)成員變量和普通成員變量
- 成員函數(shù):靜態(tài)成員函數(shù)和普通成員函數(shù)
雖然為了集成,我們將其寫(xiě)到一個(gè)類(lèi)里面,但是只有普通成員變量真正屬于類(lèi)的對(duì)象,類(lèi)的所有對(duì)象共享一份靜態(tài)成員函數(shù),靜態(tài)成員變量和普通成員函數(shù)。畫(huà)出了內(nèi)存模型,如下圖所示:

為了進(jìn)一步理解,我們舉例如下:
2.1、只定義成員函數(shù)
class person{
public:
// 定義一個(gè)空的構(gòu)造函數(shù)
person(int m_age, int m_ID){
}
};
person p1(10, 20);
cout << "p1 所占的空間為:" << sizeof(p1) << endl;
輸出結(jié)果為:
p1 所占的空間為:1
這個(gè)題目在《劍指offer》一書(shū)中也提到過(guò),由空類(lèi)實(shí)例化出來(lái)的對(duì)象所占的內(nèi)存空間是1個(gè)而不是0個(gè)字節(jié),因?yàn)榫幾g其給對(duì)象 p1 分配了一個(gè)地址,來(lái)表示不同的對(duì)象存儲(chǔ)在不同的地址空間,因此占用1個(gè)字節(jié)。
2.2、往空類(lèi)中添加靜態(tài)成員變量
class person{
static int age; //靜態(tài)成員變量,存在全局區(qū),不屬于類(lèi)對(duì)象的一部分
static int ID; //靜態(tài)成員變量,存在全局區(qū),不屬于類(lèi)對(duì)象的一部分
public:
// 定義一個(gè)空的構(gòu)造函數(shù)
person(int m_age, int m_ID){
}
};
person p1(10, 20);
cout << "p1 所占的空間為:" << sizeof(p1) << endl;
輸出結(jié)果為:
p1 所占的空間為:1
當(dāng)向類(lèi)中加入了成員函數(shù)與靜態(tài)成員變量時(shí),類(lèi)的實(shí)例化對(duì)象仍然只占用1個(gè)字節(jié)的空間,足以證明這些函數(shù)和變量并不是類(lèi)對(duì)象的一部分。
2.3、再加入非靜態(tài)成員變量
class person
{
static int age; //靜態(tài)成員變量,存在全局區(qū),不屬于類(lèi)對(duì)象的一部分
static int ID; //靜態(tài)成員變量,存在全局區(qū),不屬于類(lèi)對(duì)象的一部分
int a; //非靜態(tài)成員變量,存在棧區(qū),屬于類(lèi)對(duì)象的一部分
public:
person(int m_age, int m_ID){
}
};
輸出結(jié)果為:
p 所占的空間為:4
因此當(dāng)向類(lèi)中加入了非靜態(tài)成員變量時(shí),類(lèi)的實(shí)例化對(duì)象占用4個(gè)字節(jié)的空間,可以說(shuō)明,非靜態(tài)變量屬于類(lèi)對(duì)象的一部分。綜上:同一個(gè)類(lèi)所有實(shí)例化出來(lái)的對(duì)象共享同一份靜態(tài)成員變量,所以一改全改。既然同一個(gè)類(lèi)的不同對(duì)象共享同一份成員函數(shù),那么成員函數(shù)怎么區(qū)分該訪問(wèn)哪個(gè)對(duì)象的普通成員變量呢?
三、this指針
接著上一小節(jié)的問(wèn)題,this指針為上述問(wèn)題提供了一個(gè)完美的解決方案,它指向用來(lái)調(diào)用成員函數(shù)的對(duì)象(被當(dāng)作參數(shù)隱式地傳遞給成員函數(shù)),我們通過(guò)一張圖來(lái)理解它:

此外,this指針的另一個(gè)用途是當(dāng)成員函數(shù)需要返回對(duì)象時(shí),用 return *this; 或者 return this,這種做法能夠?qū)崿F(xiàn)鏈?zhǔn)骄幊?。比如?/p>
p2.addPerson(p1).addPerson(p1);
首先,對(duì)象 p2 調(diào)用成員函數(shù) addPerson(p1) ,其返回值繼續(xù)調(diào)用 addPerson(p1),此時(shí)返回值就必須也是 person 類(lèi)型才可以,因此使用 this 指針可以完成需求。先來(lái)看第一個(gè)例子:
class person{
public:
int age;
person(int age)
{
this->age = age; // this指針區(qū)分調(diào)用者
}
// 返回值為person類(lèi)型,且參數(shù)加上了const限制,防止修改原數(shù)據(jù)
person addPerson(const person& p)
{
this->age += p.age; // 主要實(shí)現(xiàn)兩個(gè)類(lèi)對(duì)象年齡的相加
return *this; // 由于返回值是person,因此返回 *this
}
};
person p1(20);
person p2(10);
person p3 = p2.addPerson(p1).addPerson(p1);
cout << "p1 age:" << p1.age << endl;
cout << "p2 age:" << p2.age << endl;
cout << "p3 age:" << p3.age << endl;
首先,通過(guò)構(gòu)造函數(shù)分別對(duì) p1,p2 賦了初值,然后 p2 調(diào)用函數(shù) addPerson(p1) 修改自身的變量 age 。**由于函數(shù)通過(guò)值傳遞的方式返回 person 類(lèi)型,所以將整個(gè) person 類(lèi)型復(fù)制了一份返回,返回值繼續(xù)調(diào)用 addPerson(p1), **最后的結(jié)果賦值給了新的對(duì)象 p3。所以輸出結(jié)果為:
p1 age:20
p2 age:30
p3 age:50
但是如果函數(shù) addPerson() 修改為:
person& addPerson(const person& p)
{
this->age += p.age; // 主要實(shí)現(xiàn)兩個(gè)類(lèi)對(duì)象年齡的相加
return *this; // 雖然返回值是person&,返回值的類(lèi)型也是 *this
}
person p1(20);
person p2(10);
person p3 = p2.addPerson(p1).addPerson(p1);
cout << "p1 age:" << p1.age << endl;
cout << "p2 age:" << p2.age << endl;
cout << "p3 age:" << p3.age << endl;
與上例唯一的區(qū)別就在于返回值的類(lèi)型變成了引用,那么每次返回的就變成了該對(duì)象本身,而非在值傳遞中拷貝出來(lái)的那一份數(shù)據(jù)。那么輸出就變成了:
p1 age:20
p2 age:50
p3 age:50
四、析構(gòu)函數(shù)
用構(gòu)造函數(shù)創(chuàng)建對(duì)象后,程序負(fù)責(zé)跟蹤該對(duì)象,知道其過(guò)期為止。當(dāng)對(duì)象過(guò)期時(shí),程序自動(dòng)調(diào)用析構(gòu)函數(shù)完成清理工作。與構(gòu)造函數(shù)一樣,C++默認(rèn)提供了一個(gè)空的析構(gòu)函數(shù),定義為:~類(lèi)名( )。由于開(kāi)辟在棧區(qū)的變量程序會(huì)自動(dòng)釋放,因此不需要析構(gòu)函數(shù)執(zhí)行清理工作,但是當(dāng)程序員在堆區(qū)開(kāi)辟空間時(shí),需要手動(dòng)執(zhí)行清理工作,這時(shí)候需要析構(gòu)函數(shù)來(lái)釋放堆區(qū)內(nèi)存。比如:
~person()
{
// 在析構(gòu)函數(shù)內(nèi)寫(xiě)入需要執(zhí)行的代碼
cout << "調(diào)用析構(gòu)函數(shù)" << endl;
}
person p1(20);
person p2(10); // 在生命周期結(jié)束后自動(dòng)調(diào)用析構(gòu)函數(shù)執(zhí)行清理工作
輸出為:
調(diào)用析構(gòu)函數(shù)
調(diào)用析構(gòu)函數(shù)
以上就是正確理解C++的構(gòu)造函數(shù)和析構(gòu)函數(shù)的詳細(xì)內(nèi)容,更多關(guān)于C++ 構(gòu)造函數(shù) 析構(gòu)函數(shù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- C++類(lèi)與對(duì)象深入之構(gòu)造函數(shù)與析構(gòu)函數(shù)詳解
- C++超詳細(xì)講解析構(gòu)函數(shù)
- C++超詳細(xì)講解構(gòu)造函數(shù)與析構(gòu)函數(shù)的用法及實(shí)現(xiàn)
- C++分析類(lèi)的對(duì)象作類(lèi)成員調(diào)用構(gòu)造與析構(gòu)函數(shù)及靜態(tài)成員
- C++深入講解對(duì)象的銷(xiāo)毀之析構(gòu)函數(shù)
- C++編程析構(gòu)函數(shù)拷貝構(gòu)造函數(shù)使用示例詳解
- C++中構(gòu)造函數(shù)與析構(gòu)函數(shù)的詳解及其作用介紹
- C++踩坑實(shí)戰(zhàn)之構(gòu)造和析構(gòu)函數(shù)
- C++淺析析構(gòu)函數(shù)的特征
相關(guān)文章
深入HRESULT與Windows Error Codes的區(qū)別詳解
本篇文章是對(duì)HRESULT與Windows Error Codes的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
C語(yǔ)言實(shí)現(xiàn)求梅森素?cái)?shù)的代碼與解析
這篇文章主要給大家介紹了關(guān)于利用C語(yǔ)言實(shí)現(xiàn)求梅森素?cái)?shù)的代碼與解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-12-12
C++實(shí)現(xiàn)中綴表達(dá)式轉(zhuǎn)化為后綴表達(dá)式詳解
這篇文章主要為大家詳細(xì)介紹了如何利用C++解決實(shí)現(xiàn)中綴表達(dá)式轉(zhuǎn)換為后綴表達(dá)式的問(wèn)題,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
C++中實(shí)現(xiàn)矩陣的加法和乘法實(shí)例
這篇文章主要介紹了C++中實(shí)現(xiàn)矩陣的加法和乘法實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-03-03

