文章目錄
- 前言
- 一、this指針
- 1.1、this指針的引出
- 1.2、 this指針的特性
- 二、類的默認的六個構造函數
- 2.1、構造函數簡述
- 2.2構造函數
- 三、析構函數
- 3.1、析構函數引出
- 3.2、特點:
- 四、拷貝構造
- 4.1、引入
- 4.2、特征:
- 4.3、默認拷貝構造函數
- 總結
前言
在本節中,我將給大家介紹我們在學習C++中經常要用到的,this指針、類的六個默認成員函數、運算符重載
等相關知識.
下面我會結合一個簡單的類----日期類來介紹
后面需要時會補充相應的成員函數
class Date
{
public:void Display()//打印類對象中的內容{cout << _year << "-" << _month << "-" << _day << endl;}void SetDate(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year; // 年int _month; // 月int _day; // 日
};
一、this指針
1.1、this指針的引出
int main(){Date d1, d2;d1.SetDate(2024,5,1);d2.SetDate(2024,6,1);d1.Display();d2.Display();return 0;}
當執行上述代碼時,它的輸出結果為:
下面的匯編不了解的,可以搜一下棧幀的創建和銷毀,對今后的學習幫助很大
通過匯編我們可以看到,兩次調用的SetDat函數地址相同(調用同一函數)
對于上述類,有這樣的一個問題:
Date類中有SetDate與Display兩個成員函數,函數體中沒有關于不同對象的區分,那當d1調用SetDate函數時,該函數是如何知道應該設置s1對象,而不是設置d2對象呢?
C++中通過引入this指針解決該問題,即:C++編譯器給每個“非靜態的成員函數“增加了一個隱藏的指針參數,讓該指針指向當前對象(函數運行時調用該函數的對象),在函數體中所有成員變量的操作,都是通過該指針去訪問。只不過所有的操作對用戶是透明的,即用戶不需要來傳遞,編譯器自動完成。
1.2、 this指針的特性
- this指針只能在“成員函數”的內部使用
- this指針本質上其實是一個成員函數的形參,是對象調用成員函數時,將對象地址作為實參傳遞給this形參。
- this指針是成員函數第一個隱含的指針形參,不需要用戶
傳遞.
將調用成員函數展開:
下面我們來證明一下this指針,本質上就是對象的地址:
為了更清晰的展示,我會簡化用不到的代碼
class Date
{
public:void Print_this(){cout << this << endl;//打印this指針}
private:int _year; // 年int _month; // 月int _day; // 日
};
int main()
{Date d1, d2;d1.Print_this();//cout << &d1 << endl;//如果和我們說的一樣,那么打印結果應該兩兩相同d2.Print_this();cout << &d2 << endl;return 0;
}
結果:
可以看到this本質就是對象的地址。這就是隱藏的this指針,當然我們可以像學習C語言時知道一個結構體對象的地址使用“->”來進行成員變量的訪問
class Date{public:void Display(){cout <<this-> _year << "-" <<this-> _month << "-" << this->_day << endl;}private:int _year; // 年int _month; // 月int _day; // 日};
二、類的默認的六個構造函數
恩師莫怪
2.1、構造函數簡述
如果一個類中什么成員都沒有,簡稱為空類。空類中什么都沒有嗎?并不是的,任何一個類在我們不寫的情況下,編譯器都會自動生成6個默認成員函數。這些函數叫做構造函數。構造函數的任務是初始化類對象的數據成員,無論何時只要類的對象被創建,就會執行構造函數。
構造函數特點:
1.構造函數的名字和類名相同。
2.和其他函數不一樣的是,構造函數沒有返回類型。
3.類似于其他的函數,構造函數也有一個(可能為空的)參數列表和一個(可能為空的)函數體。
4.構造函數不能被聲明成const 的。
需要特別注意的是,一個類可以擁有多個參數不同的構造函數,這些構造函數之間,向普通函數之間一樣,可以構成函數重載
2.2構造函數
再貼一遍方便大家看
class Date
{
public:void Display()//打印類對象中的內容{cout << _year << "-" << _month << "-" << _day << endl;}void SetDate(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year; // 年int _month; // 月int _day; // 日
};
對于Date類,可以通過SetDate公有的方法給對象設置內容,但是如果每次創建對象都調用該方法設置信息,未免有點麻煩,那能否在對象創建時,就將信息設置進去呢?我們上面說過:創建類的類型對象時由編譯器自動調構造函數,保證每個數據成員都有 一個合適的初始值,并且在對象的生命周期內只調用一次。
class Date
{
public:// 1.無參構造函數Date(){}// 2.帶參構造函數Date(int year, int month, int day)//無返回值{_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
void TestDate()
{Date d1; // 調用無參構造函數Date d2(2024, 6, 1); // 調用帶參的構造函數,用于初始化// 注意:如果通過無參構造函數創建對象時,對象后面不用跟括號,否則就成了函數聲明// 以下代碼的函數:聲明了d3函數,該函數無參,返回一個日期類型的對象Date d3();
如果類中沒有顯式定義構造函數,則C++編譯器會自動生成一個無參的默認構造函數,一旦用戶顯式定義編譯器將不再生成。
1.證明編譯器默認生成的為無參的構造函數:
可以看到,我將自己寫的構造函數屏蔽后,調用有參的構造函數是無法成功的,無參的并沒有報錯,這也說明編譯器默認生成的為無參的構造函數。
2.證明當我們顯示寫出一個構造函數,編譯器就不會在生成默認的構造函數:
我將帶參的構造函數顯示定義,d1無法調用無參的默認構造函數。
無參的構造函數和全缺省的構造函數都稱為默認構造函數,并且這兩個默認構造函數不能同時存在。注意:無參構造函數、全缺省構造函數、我們沒寫編譯器默認生成的構造函數,都可以認為是默認成員函數.
默認構造函數我們一般是為了處理自定義類型的成員變量
三、析構函數
3.1、析構函數引出
前面通過構造函數的學習,我們知道一個對象時怎么來的,那一個對象又是怎么沒呢的?
析構函數:與構造函數功能相反,析構函數不是完成對象的銷毀,局部對象銷毀工作是由編譯器完成的。而對象在銷毀時會自動調用析構函數,完成類的一些資源清理工作(比如:類中定義的指針指向的空間的清理)
3.2、特點:
- 析構函數名是在類名前加上字符 ’~‘。
- 無參數無返回值。
- 一個類有且只有一個析構函數。若未顯式定義,系統會自動生成默認的析構函數。
- 對象生命周期結束時,C++編譯系統系統自動調用析構函數
![class Date
{
public:~Date(){_year = 0;_month = 0;_day = 0;} Date(int year, int month, int day){_year = year;_month = month;_day = day;}int _year;int _month;int _day;
};
int main()
{Date d2(2024, 6, 1);return 0;
}
反匯編視角:
在上面的代碼中,我并沒有去顯示調用析構函數,但是在程序執行結束時,編譯器自動調用了,析構函數。
這個也可以像構造函數一樣測試,大家嘗試一下
四、拷貝構造
4.1、引入
拷貝構造函數是構造函數的一種重載形式,它可以用來創建一個與已存在的對象一模一樣的新對象。對于拷貝構造,它只有單個形參,且該形參必須是對本類類型對象的引用,因為要引用,所以要加const修飾。
4.2、特征:
1.拷貝構造函數的參數若使用傳值方式編譯器直接報錯, 因為會引發無窮遞歸調用。
2.若未顯式定義,編譯器會生成默認的拷貝構造函數。 默認的拷貝構造函數,對對象按 字節序完成拷貝,這種拷貝叫做淺拷貝,或者值拷貝。
3.編譯器生成的默認拷貝構造函數已經可以完成字節序的值拷貝了。
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1)//全缺省{_year = year;_month = month;_day = day;}// Date(const Date d) // 錯誤--引發無窮遞歸Date(const Date& d) // 正確寫法_year = d._year;_month = d._month;_day = d._day;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;Date d2(d1);//也可以寫成Date d2=d1return 0;
}
4.3、默認拷貝構造函數
像上面介紹的默認成員函數一樣,當我們沒有在類中寫拷貝構造函數時,編譯器會自動生成一個默認的拷貝構造。
系統生成的拷貝構造也會針對成員變量的內置類型和自定義類型做一個區分。對于內置類型的成員變量,編譯器會按照被拷貝對象的內存存儲字節序完成拷貝,就好比被拷貝的對象有3個int類型成員變量,占12字節內存,編譯器會根據該對象的內存和成員初始值拷貝給新對象。
總結
本次我們介紹了,this指針、構造函數、析構函數、拷貝構造、等一些與類緊密關聯的知識。
要將他們詳細的介紹,所用篇幅過長,后續會出拓展版的