今天要學習兩個特殊的函數,分別是構造函數和析構函數,它們究竟有什么用呢?
比如說,我們先寫一個簡單的日期的類
class Date {
public:void Init() {_year = 1;_month = 1;_day = 1;}void Print() {cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};int main() {Date d1;d1.Init();d1.Print();return 0;
}
我們要定義一個對象后還要對這個對象進行初始化,只要定義對象就要初始化,這個步驟是不是有點太繁瑣了,甚至我們有時候還會忘記初始化,所以這時,就創造了一種函數叫做構造函數,它其實就是去完成了初始化這個工作,并且它是在對象創建后自動調用的,是不是就讓我們省心了不少
它的一些規則是這樣的:
1.函數名與類名相同,意思是在日期這個例子中,構造函數的函數名也叫Date
2.無返回值,這里的無返回值意思不是寫void,而是根本就不需要寫返回值
3.對象實例化(創建對象)時編譯器自動調用對應的構造函數
4.構造函數可以重載
比如說我去寫一個構造函數
class Date {
public:Date(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(2023,11,22);d1.Print();return 0;
}
這時就可以替換掉Init函數,并且調用的話也是這么去調用,規則的第四條是什么意思呢?構成重載嘛,就是構成重載的函數可以同時存在,比如說
Date() {_year = 1;_month = 1;_day = 1;}Date(int year,int month,int day) {_year = year;_month = month;_day = day;}
當我去傳參調用的話就調用第二個,當我不傳參調用就調用第一個,不會產生歧義
我們又知道有缺省參數這回事,如果我們給第二個構造函數缺省值的話
那我不傳參調那個呢?結果是編譯出錯,因為調用有歧義
我們已經知道,如果不給構造函數的話,對于日期類(其實也就是內置類型)編譯器不會自動去調用構造函數。那如果是自定義類型呢?那編譯器就會去調用自定義類型的構造函數,如果自定義類型也沒寫構造函數,那編譯器也無法去調用。
所以在C++11中,內置類型是可以給值的,比如說
就是在聲明的時候后面給上值,這時如果沒有構造函數的話,就會用給定的值去初始化,就像這樣
這種情況我們是不是沒有寫構造函數,可是數據還是初始化了,這時我們就有了一個默認構造的概念,總的來說,我們不去傳參數調用的構造函數,都可以叫做默認構造。除了這種情況,還有無參構造函數和全缺省構造函數也可以叫默認構造。這三種情況是不能同時存在的
那我們在看看下面這種情況
構造函數不給缺省值,并且在調用的時候也不給值,這樣是會報錯的,
因為我既然不給d1傳值,但是它有構造函數,不傳參數就無法調用這個構造函數
有了所謂的初始化函數,那我們是不是也需要一個銷毀函數呢?這樣就有了我們的析構函數,這里的析構函數其實不是完成對對象本身的銷毀,而是完成對象中資源的清理工作(比如說,當一個日期類的對象生命周期結束時,它調用析構函數是沒有多大意義的,因為它的年月日隨著棧幀的銷毀就一并銷毀了;但是對于一個棧,它是在堆上申請過空間的,這時把堆上的空間要釋放掉是非常有必要的),它的規則如下
1.析構函數名是在類名前加上~
2.無參數,無返回值類型
3.一個類只能有一個析構函數,若未顯示定義,系統會自動生成默認的析構函數,并且析構函數不能重載,因為根本就沒有參數嘛
4.對象生命周期結束時,會自動調用析構函數
大概就是這樣子的
可以看到,一個對象的創建和生命周期結束確實會自動調用構造函數和析構函數
這里打印的話就是為了表示一下是否調用了該函數和什么時候調用析構函數