山西運(yùn)城網(wǎng)站開發(fā)seo就業(yè)前景如何
上次為類與對象開了一個(gè)頭:C++初階類與對象(一):學(xué)習(xí)類與對象、訪問限定符、封裝、this指針
今天就來更進(jìn)一步
文章目錄
- 1.類的6個(gè)默認(rèn)成員函數(shù)
- 2.構(gòu)造函數(shù)
- 2.1引入和概念
- 2.2構(gòu)造函數(shù)特性
- 2.2.1特性1~4
- 2.2.2注意
- 2.2.3特性5~7
- 2.2.4注意
- 3.析構(gòu)函數(shù)
- 3.1概念
- 3.2特性
1.類的6個(gè)默認(rèn)成員函數(shù)
如果一個(gè)類中什么成員都沒有,簡稱為空類。
空類中并不是什么都沒有,任何類在什么都不寫時(shí),編譯器會(huì)自動(dòng)生成上面6個(gè)默認(rèn)成員函數(shù)。
默認(rèn)成員函數(shù):用戶沒有顯式實(shí)現(xiàn),編譯器會(huì)生成的成員函數(shù)稱為默認(rèn)成員函數(shù)
今天就來主要聚焦于構(gòu)造函數(shù)和析構(gòu)函數(shù)
2.構(gòu)造函數(shù)
2.1引入和概念
class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year; // 年int _month; // 月int _day; // 日
};int main()
{Date d1;d1.Init(2023, 10, 19);d1.Print(); return 0;
}
每次實(shí)例化對象后,都要自己進(jìn)行初始化(使用成員函數(shù)Init),但如果每次創(chuàng)建對象時(shí)都調(diào)用該方法設(shè)置信息,未免有點(diǎn)麻煩,時(shí)不時(shí)還會(huì)忘記初始化。所以構(gòu)造函數(shù)來了:
構(gòu)造函數(shù)是一個(gè)特殊的成員函數(shù),名字與類名相同,創(chuàng)建類類型對象時(shí)由編譯器自動(dòng)調(diào)用,以保證每個(gè)數(shù)據(jù)成員都有 一個(gè)合適的初始值,并且在對象整個(gè)生命周期內(nèi)只調(diào)用一次
2.2構(gòu)造函數(shù)特性
2.2.1特性1~4
構(gòu)造函數(shù)是特殊的成員函數(shù),需要注意的是,構(gòu)造函數(shù)雖然名稱叫構(gòu)造,但是構(gòu)造函數(shù)的主要任務(wù)并不是開空間創(chuàng)建對象,而是初始化對象
其特征如下:
- 函數(shù)名與類名相同。
- 無返回值(不是寫void,就是真真正正的沒有)。
- 對象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用對應(yīng)的構(gòu)造函數(shù)。
- 構(gòu)造函數(shù)可以重載
class Date
{
public:Date() //無參構(gòu)造函數(shù){ _year = 1;_month = 1;_day = 1;}Date(int year, int month, int day)//有參構(gòu)造函數(shù) ,二者構(gòu)成重載{_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year; // 年int _month; // 月int _day; // 日
};int main()
{//Date d1(); 這樣不能調(diào)用無參初始化Date d1;//調(diào)用無參構(gòu)造函數(shù)d1.Print(); Date d2(2024, 1, 3);//調(diào)用帶參構(gòu)造函數(shù)d2.Print();return 0;
}
2.2.2注意
- 調(diào)用無參初始化時(shí)不能加上括號:加上后與函數(shù)聲明無法區(qū)分開
- 對有參構(gòu)造函數(shù)使用全缺省可以代替無參構(gòu)造函數(shù),此時(shí)雖然二者依然構(gòu)成重載,但是初始化對象不知道調(diào)用哪一個(gè)。此時(shí)只需要一個(gè)全缺省構(gòu)造函數(shù)就可以
class Date { public:Date(int year=1, int month=1, int day=1)//有參構(gòu)造函數(shù) ,二者構(gòu)成重載{_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year; // 年int _month; // 月int _day; // 日 };int main() {Date d1;//調(diào)用帶參構(gòu)造函數(shù),全缺省d1.Print();Date d2(2024, 1, 3);//調(diào)用帶參構(gòu)造函數(shù)d2.Print();Date d3(2024);//傳部分也行d3.Print();return 0; }
2.2.3特性5~7
- 如果類中沒有顯式定義構(gòu)造函數(shù),則C++編譯器會(huì)自動(dòng)生成一個(gè)無參的默認(rèn)構(gòu)造函數(shù),一旦用戶顯式定義編譯器將不再生成
如果Date類有自己定義的構(gòu)造函數(shù),代碼編譯失敗,因?yàn)橐坏╋@式定義任何構(gòu)造函數(shù),編譯器將不再生成默認(rèn)構(gòu)造函數(shù)
class Date
{
public://此時(shí)沒有進(jìn)行顯示構(gòu)造函數(shù)定義,會(huì)使用系統(tǒng)默認(rèn)生成的無參構(gòu)造函數(shù)void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year; // 年int _month; // 月int _day; // 日
};int main()
{Date d1;//調(diào)用默認(rèn)構(gòu)造函數(shù)d1.Print();return 0;
}
為什么是隨機(jī)值呢?這就牽扯到了特性6了
- 關(guān)于編譯器生成的默認(rèn)成員函數(shù),大家會(huì)有疑惑:不實(shí)現(xiàn)構(gòu)造函數(shù)的情況下,編譯器會(huì)生成默認(rèn)的構(gòu)造函數(shù)。但是看起來默認(rèn)構(gòu)造函數(shù)又沒什么用(上面那個(gè)例子都是隨機(jī)值)?
d1
對象調(diào)用了編譯器生成的默認(rèn)構(gòu)造函數(shù),但是d1
對象_year/_month/_day
,依舊是隨機(jī)值。也就說在這里編譯器生成的默認(rèn)構(gòu)造函數(shù)并沒有什么用?解答:C++把類型分成內(nèi)置類型(基本類型)和自定義類型。內(nèi)置類型就是語言提供的數(shù)據(jù)類型,如:int/char…,自定義類型就是我們使用class/struct/union等自己定義的類型,看看下面的程序,就會(huì)發(fā)現(xiàn)編譯器生成默認(rèn)的構(gòu)造函數(shù)會(huì)對自定類型成員_t調(diào)用的它的默認(rèn)構(gòu)造函數(shù),對于內(nèi)置類型(如int、float、指針等),默認(rèn)構(gòu)造函數(shù)不會(huì)對其進(jìn)行任何初始化操作,這意味著它們的值將是未定義的,可能是隨機(jī)的
class Stack
{
public:Stack(int capacity = 3){_a = (int*)malloc(sizeof(int) * capacity);assert(_a);_capacity = capacity;_top = -1;}private:int* _a;int _top;int _capacity;//這三個(gè)是內(nèi)置類型
};class Queue
{private:Stack _push;//這兩個(gè)棧都是自定義類型Stack _pop;
};int main()
{Stack s1;Queue q1;return 0;
}
可以看到兩個(gè)自定義類型的Stack變量都進(jìn)行了初始化,而且調(diào)用了Stack的構(gòu)造函數(shù),如果Stack沒有給構(gòu)造函數(shù),那Queue也是隨機(jī)值了
- 無參的構(gòu)造函數(shù)和全缺省的構(gòu)造函數(shù)都稱為默認(rèn)構(gòu)造函數(shù),并且默認(rèn)構(gòu)造函數(shù)只能有一個(gè)。注意:無參構(gòu)造函數(shù)、全缺省構(gòu)造函數(shù)、我們沒寫編譯器默認(rèn)生成的構(gòu)造函數(shù),都可以認(rèn)為是默認(rèn)構(gòu)造函數(shù)
2.2.4注意
- C++11 中針對內(nèi)置類型成員不初始化的缺陷,又打了補(bǔ)丁,即:內(nèi)置類型成員變量在類中聲明時(shí)可以給默認(rèn)值,可以用來對內(nèi)置類型進(jìn)行初始化
- 指針都是內(nèi)置類型(Date d1也是)*
class Stack
{
public://Stack(int capacity = 3)//{// _a = (int*)malloc(sizeof(int) * capacity);// assert(_a);// _capacity = capacity;// _top = -1;//}private:int* _a=nullptr;int _top=-1;int _capacity=1;//這三個(gè)是內(nèi)置類型
};int main()
{Stack s1;return 0;
}
3.析構(gòu)函數(shù)
3.1概念
析構(gòu)函數(shù):與構(gòu)造函數(shù)功能相反,析構(gòu)函數(shù)不是完成對對象本身的銷毀,局部對象銷毀工作是由編譯器完成的。而對象在銷毀時(shí)會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù),完成對象中資源的清理工作
3.2特性
析構(gòu)函數(shù)是特殊的成員函數(shù),其特征如下:
- 析構(gòu)函數(shù)名是在類名前加上字符
~
。- 無參數(shù)無返回值類型(跟構(gòu)造函數(shù)一樣)。
- 一個(gè)類只能有一個(gè)析構(gòu)函數(shù)。若未顯式定義,系統(tǒng)會(huì)自動(dòng)生成默認(rèn)的析構(gòu)函數(shù)。注意:析構(gòu)函數(shù)不能重載
- 對象生命周期結(jié)束時(shí)(出了作用域),C++編譯系統(tǒng)系統(tǒng)自動(dòng)調(diào)用析構(gòu)函數(shù)
class Date
{
public://此時(shí)沒有進(jìn)行顯示構(gòu)造函數(shù)定義,會(huì)使用系統(tǒng)默認(rèn)生成的無參構(gòu)造函數(shù)void Print(){cout << _year << "-" << _month << "-" << _day << endl;}~Date(){cout << "調(diào)用了析構(gòu)函數(shù)";}private:int _year=1; // 年int _month=1; // 月int _day=1; // 日
};int main()
{Date d1;d1.Print();return 0;
}
- 關(guān)于編譯器自動(dòng)生成的析構(gòu)函數(shù),是否會(huì)完成一些事情呢?編譯器生成的默認(rèn)析構(gòu)函數(shù),對自定類型成員調(diào)用它的析構(gòu)函數(shù)。對內(nèi)置類型不進(jìn)行處理
class Time
{
public:~Time(){cout << "調(diào)用了time的析構(gòu)函數(shù)" << endl;}
private:int _hour;int _minute;int _second;
};class Date
{
private:// 基本類型(內(nèi)置類型)int _year = 1;int _month = 1;int _day = 1;// 自定義類型Time _time;
};int main()
{Date d1;return 0;
}
程序運(yùn)行結(jié)束后輸出:調(diào)用了time的析構(gòu)函數(shù)
在main方法中根本沒有直接創(chuàng)建Time類的對象,為什么最后會(huì)調(diào)用Time類的析構(gòu)函數(shù)?
因?yàn)?#xff1a;main方法中創(chuàng)建了Date對象d,而d中包含4個(gè)成員變量,其中
_year, _month, _day
三個(gè)是內(nèi)置類型成員,銷毀時(shí)不需要資源清理,最后系統(tǒng)直接將其內(nèi)存回收即可;而
_time
是Time類對象,所以在 d銷毀時(shí),要將其內(nèi)部包含的Time類的_time
對象銷毀,所以要調(diào)用Time類的析構(gòu)函數(shù)。但是:main函數(shù) 中不能直接調(diào)用Time類的析構(gòu)函數(shù),實(shí)際要釋放的是Date類對象,所以編譯器會(huì)調(diào)用Date類的析構(gòu)函數(shù),而Date沒有顯式提供,則編譯器會(huì)給Date類生成一個(gè)默認(rèn)的析構(gòu)函數(shù),目的是在其內(nèi)部調(diào)用Time類的析構(gòu)函數(shù),即當(dāng)Date對象銷毀時(shí),要保證其內(nèi)部每個(gè)自定義對象都可以正確銷毀main函數(shù)中并沒有直接調(diào)用Time類析構(gòu)函數(shù),而是顯式調(diào)用編譯器為Date類生成的默認(rèn)析構(gòu)函數(shù)
注意:創(chuàng)建哪個(gè)類的對象則調(diào)用該類的析構(gòu)函數(shù),銷毀那個(gè)類的對象則調(diào)用該類的析構(gòu)函數(shù)
- 如果類中沒有申請資源時(shí),析構(gòu)函數(shù)可以不寫,直接使用編譯器生成的默認(rèn)析構(gòu)函數(shù),比如Date類;有資源申請時(shí),一定要寫,否則會(huì)造成資源泄漏(內(nèi)存泄漏),比如Stack類
好啦,這次的內(nèi)容就先到這里啦!期待下次與大家分享!!!
我的博客即將同步至騰訊云開發(fā)者社區(qū),邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=1pao3hba2ho4v