C++修煉之構(gòu)造函數(shù)與析構(gòu)函數(shù)
??默認(rèn)成員函數(shù)
上一章中我們談到,如果一個類中什么成員也沒有,那么這個類就叫作空類。其實這么說是不太嚴(yán)謹(jǐn)?shù)?,因?code>一個類不可能什么都沒有。
當(dāng)我們定義好一個類,不做任何處理時,編譯器會自動生成以下6個默認(rèn)成員函數(shù):
默認(rèn)成員函數(shù):如果用戶沒有手動實現(xiàn),則編譯器會自動生成的成員函數(shù)
構(gòu)造函數(shù):主要完成初始化工作;析構(gòu)函數(shù):主要完成清理工作;拷貝構(gòu)造:使用一個同類的對象初始化創(chuàng)建一個對象;賦值重載:把一個對象賦值給另一個對象;取地址重載:普通對象取地址操作;取地址重載(const):const對象取地址操作;
本章我們將學(xué)習(xí)兩個默認(rèn)成員函數(shù)——構(gòu)造函數(shù)與析構(gòu)函數(shù)。
??構(gòu)造函數(shù)
??引例
在C語言階段,我們實現(xiàn)棧的數(shù)據(jù)結(jié)構(gòu)時,有一件事很苦惱,就是每當(dāng)創(chuàng)建一個stack對象(之前叫作定義一個stack類型的變量)后,首先得調(diào)用它的專屬初始化函數(shù)StackInit來初始化對象。
typedef int dataOfStackType;
typedef struct stack
{
dataOfStackType* a;
int top;
int capacity;
}stack;
void StackInit(stack* ps);
//...
int main()
{
stack s;
StackInit(&s);
//...
return 0;
}
這不免讓人覺得有點麻煩。在C++中,構(gòu)造函數(shù)為我們很好的解決了這一問題。
??構(gòu)造函數(shù)的概念及特性
構(gòu)造函數(shù)是一個特殊的成員函數(shù)。構(gòu)造函數(shù)雖然叫作構(gòu)造,但是其主要作用并不是開辟空間創(chuàng)建對象,而是初始化對象。
構(gòu)造函數(shù)之所以特殊,是因為相比于其它成員函數(shù),它具有如下特性:
- 函數(shù)名與類名相同;
- 無返回值;
- 對象實例化時,編譯器
自動調(diào)用對應(yīng)的構(gòu)造函數(shù); - 構(gòu)造函數(shù)可以重載;
??舉例??
class Date
{
public:
//無參的構(gòu)造函數(shù)
Date()
{};
//帶參的構(gòu)造函數(shù)
Date(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
void TestDate()
{
Date d1;//調(diào)用無參構(gòu)造函數(shù)(自動調(diào)用)
Date d2(2023, 3, 29);//調(diào)用帶參構(gòu)造函數(shù)(自動調(diào)用)
}
??特別注意??
- 創(chuàng)建對象時編譯器會自動調(diào)用構(gòu)造函數(shù),若是
調(diào)用無參構(gòu)造函數(shù),則無需在對象后面使用()。否則會產(chǎn)生歧義:編譯器無法確定你是在聲明函數(shù)還是在創(chuàng)建對象。
??錯誤示例??
//錯位示例 Date d3();
- 如果類中沒有顯式定義構(gòu)造函數(shù),則C++編譯器會自動生成一個無參的默認(rèn)構(gòu)造函數(shù),一旦用戶顯式定義編譯器將不再生成。
class Date
{
public:
//若用戶沒有顯示定義,則編譯器自動生成。
/*Date(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
}*/
private:
int _year;
int _month;
int _day;
};
- 默認(rèn)生成構(gòu)造函數(shù),對內(nèi)置類型成員不作處理;對自定義類型成員,會調(diào)用它的默認(rèn)構(gòu)造函數(shù);
- C++把類型分成
內(nèi)置類型(基本類型)和自定義類型。內(nèi)置類型就是語言提供的數(shù)據(jù)類型,如:int、char、double…,自定義類型就是我們使用class、struct、union等自己定義的類型。
??舉例??
??默認(rèn)構(gòu)造函數(shù)對內(nèi)置類型??
class Date
{
public:
//此處不對構(gòu)造函數(shù)做顯示定義,測試默認(rèn)構(gòu)造函數(shù)
/*Date()
{}*/
void print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
void TestDate1()
{
Date d1;
d1.print();
}

- 如圖所示,默認(rèn)構(gòu)造函數(shù)的確未對內(nèi)置類型做處理。
??默認(rèn)構(gòu)造函數(shù)對自定義類型??
class stack
{
public:
//此處對stack構(gòu)造函數(shù)做顯示定義
stack()
{
cout <<"stack()" << endl;
_a = nullptr;
_top = _capacity = 0;
}
private:
int* _a;
int _top;
int _capacity;
};
class queue
{
public:
//此處不對queue構(gòu)造函數(shù)做顯示定義,測試默認(rèn)構(gòu)造函數(shù)
/*queue()
{}*/
private:
//自定義類型成員
stack _s;
};
void TestQueue()
{
queue q;
}

- 如圖所示,在創(chuàng)建
queue對象時,默認(rèn)構(gòu)造函數(shù)對自定義成員_s做了處理,調(diào)用了它的默認(rèn)構(gòu)造函數(shù)stack()。
這一波蜜汁操作讓很多C++使用者感到困惑與不滿,為什么要針對內(nèi)置類型和自定義類型做不同的處理呢?終于,在C++11中針對內(nèi)置類型成員不初始化的缺陷,又打了補丁,即:
- 內(nèi)置類型成員變量在類中聲明時可以給默認(rèn)值;
??舉例??
class Date
{
public:
//...
void print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
//使用默認(rèn)值
int _year = 0;
int _month = 0;
int _day = 0;
};
void TestDate2()
{
Date d2;
d2.print();
}

默認(rèn)值:若不對成員變量做處理,則使用默認(rèn)值。
- 無參的構(gòu)造函數(shù)和全缺省的構(gòu)造函數(shù)都稱為默認(rèn)構(gòu)造函數(shù),并且默認(rèn)構(gòu)造函數(shù)只能有一個;
??舉例??
class Date
{
public:
//無參的默認(rèn)構(gòu)造函數(shù)
//Date()
//{
//}
//全缺省的默認(rèn)構(gòu)造函數(shù)
Date(int year = 0, int month = 0, int day = 0)
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year = 0;
int _month = 0;
int _day = 0;
};
??析構(gòu)函數(shù)
析構(gòu)函數(shù)與構(gòu)造函數(shù)的特性相似,但功能有恰好相反。構(gòu)造函數(shù)是用來初始化對象的,析構(gòu)函數(shù)是用來銷毀對象的。
- 需要注意的是,
析構(gòu)函數(shù)并不是對對象本身進行銷毀(因為局部對象出了作用域會自行銷毀,由編譯器來完成),而是在對象銷毀時會自動調(diào)用析構(gòu)函數(shù),對對象內(nèi)部的資源做清理(例如stack _s中的int* a)。
同樣,有了析構(gòu)函數(shù),我們再也不用擔(dān)心創(chuàng)建對象(或定義變量)后由于忘記釋放內(nèi)存而造成內(nèi)存泄漏了。
??舉例??
class Stack
{
public:
Stack()
{
//...
}
void Push(int x)
{
//...
}
bool Empty()
{
// ...
}
int Top()
{
//...
}
void Destory()
{
//...
}
private:
// 成員變量
int* _a;
int _top;
int _capacity;
};
void TestStack()
{
Stack s;
st.Push(1);
st.Push(2);
//過去需要手動釋放
st.Destroy();
}
??析構(gòu)函數(shù)的特性
- 析構(gòu)函數(shù)名是在類名前加上字符
~; - 無參數(shù);
- 無返回值;
- 一個類只能有一個析構(gòu)函數(shù)。若未顯式定義,系統(tǒng)會自動生成默認(rèn)的析構(gòu)函數(shù);
- 析構(gòu)函數(shù)不能重載;
??舉例??
class Date
{
public:
Date()
{
cout << "Date()" << endl;
}
~Date()
{
cout << "~Date()" << endl;
}
private:
int _year = 0;
int _month = 0;
int _day = 0;
};
void TestDate3()
{
Date d3;
//d3生命周期結(jié)束時自動調(diào)用構(gòu)造函數(shù)
}

為便于觀察,我們可以在析構(gòu)函數(shù)內(nèi)部寫點兒東西。
- 編譯器生成的默認(rèn)析構(gòu)函數(shù),對自定類型成員調(diào)用它的析構(gòu)函數(shù);
??舉例??
class stack
{
public:
//此處對stack構(gòu)造函數(shù)做顯示定義
stack()
{
cout <<"stack()" << endl;
_a = nullptr;
_top = _capacity = 0;
}
~stack()
{
cout << "~Stack()" << endl;
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
private:
int* _a;
int _top;
int _capacity;
};
class queue
{
public:
//此處不對queue構(gòu)造函數(shù)做顯示定義,測試默認(rèn)構(gòu)造函數(shù)
/*queue()
{}*/
private:
//自定義類型成員
stack _s;
};
void TestQueue1()
{
queue q;
}

- 這里可能有小伙伴會好奇:
為什么析構(gòu)函數(shù)不像構(gòu)造函數(shù)那樣區(qū)分內(nèi)置類型與自定義類型呢?
答案是:因為內(nèi)置類型壓根不需要我們擔(dān)心清理工作,在其生命周期結(jié)束時會自動銷毀。而自定義類型需要擔(dān)心,因為自定義類型里可能含有申請資源(例如:malloc申請內(nèi)存須手動釋放)。
- 如果類中沒有申請資源時,析構(gòu)函數(shù)可以不寫,直接使用編譯器生成的默認(rèn)析構(gòu)函數(shù),比如
Date類;有資源申請時,一定要寫,否則會造成資源泄漏,比如stack類。
到此這篇關(guān)于C++修煉之構(gòu)造函數(shù)與析構(gòu)函數(shù)的文章就介紹到這了,更多相關(guān)C語言 構(gòu)造函數(shù)與析構(gòu)函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言?智能指針?shared_ptr?和?weak_ptr
這篇文章主要介紹了C語言?智能指針?shared_ptr?和?weak_ptr,weak_ptr引入可以解決shared_ptr交叉引用時無法釋放資源的問題,下面來學(xué)習(xí)具體相關(guān)內(nèi)容吧,需要的朋友可以參考一下2022-04-04
C++實現(xiàn)LeetCode(161.一個編輯距離)
這篇文章主要介紹了C++實現(xiàn)LeetCode(161.一個編輯距離),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
C語言詳解關(guān)鍵字sizeof與unsigned及signed的用法
這篇文章主要為大家詳細(xì)介紹了C語言關(guān)鍵字sizeof&&unsigned&&signed,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-06-06

