一 類的6個默認成員函數:
如果一個類中什么成員都沒有,簡稱為空類。
例:
#include <iostream>
class Empty
{// 空類,什么成員都沒有
};
空類中真的什么都沒有嗎?并不是,任何類在什么都不寫時,編譯器會自動生成以下6個默認成員 函數。
默認構造函數:如果用戶沒有定義任何構造函數,編譯器會自動生成一個默認構造函數。
拷貝構造函數:用于創建一個對象是另一個對象的副本。如果用戶沒有定義,編譯器會生成一個默認的拷貝構造函數。
拷貝賦值運算符:用于將一個對象賦值給另一個對象。如果用戶沒有定義,編譯器會生成一個默認的拷貝賦值運算符。
移動構造函數:用于將資源從一個對象移動到另一個對象。如果用戶沒有定義,編譯器會生成一個默認的移動構造函數。
移動賦值運算符:用于將資源從一個對象移動并賦值給另一個對象。如果用戶沒有定義,編譯器會生成一個默認的移動賦值運算符。
析構函數:用于銷毀對象并釋放資源。如果用戶沒有定義,編譯器會生成一個默認的析構函數。
默認成員函數:用戶沒有顯式實現,編譯器會生成的成員函數稱為默認成員函數。
二 構造函數:
2.1:構造函數的概念:
構造函數是一個特殊的成員函數,它的名稱與類名相同,沒有返回值。在創建類的對象時,構造函數由編譯器自動調用,用于初始化對象的數據成員。
2.2:構造函數的特征
函數名與類名相同。
沒有返回值。
在對象實例化時由編譯器自動調用。
構造函數可以重載,即一個類可以有多個構造函數,只要它們的參數列表不同。
2.3:無參/有參構造函數代碼示例
class Date
{
public://有參數的構造函數://Date(int _year = 1999 , int _month = 2 , int _day = 26)// 無參構造函數:Date() //函數名與類名相同。{// 使用 this 指針訪問成員變量this->_year = 2024;this->_month = 7;this->_day = 6;}void Print(){std::cout << this->_year << "-" << this->_month << "-" << this->_day << std::endl;}private:int _year;int _month;int _day;
};int main()
{Date d1; //調用無參數構造函數 d1.Print();//Date d2(2022, 7, 6); //調用帶參構造函數//d2.Print();return 0;
}
Date()是無參的構造函數,沒有參數。在對象 d1
創建時自動調用。你們有可能會問了,為什么在無參的構造函數里面this指針指向成員變量,那為什么main函數里見不到this指針呢?因為當 Date d1;
創建對象時,編譯器會自動傳遞 d1
的地址給 this
指針所以就不需要顯示this指針。
如果類中沒有顯式定義構造函數,編譯器會自動生成一個無參的默認構造函數。一旦用戶顯式定義了任何構造函數,編譯器將不再生成無參的默認構造函數。
2.4:默認構造函數代碼示例
class Date
{
public:void Print() {std::cout << _year << "-" << _month << "-" << _day << std::endl;}private:int _year;int _month;int _day;
};int main()
{Date d1;d1.Print();return 0;
}
輸出:
上面代碼因我未顯示定義構造函數所以編譯器幫我生成了一個默認的構造函數而且是看不見的,那為什么默認生成的輸出的值是隨機值呢?
原來C++把類型分為內置類型和自定義類型,內置類型就是語言提供的基本數據類型,如int
、char
等。自定義類型是用戶定義的類型,如使用class
、struct
、union
定義的類型。
2.5:內置類型和自定義類型的默認構造函數處理
內置類型:
? ?1.內置類型的成員變量在默認構造函數中不會被自動初始化
? ?2.如果不顯式初始化,成員變量的值將是未定義的(即隨機值)
自定義類型:
? ?1.自定義類型的成員變量在默認構造函數中會調用其默認構造函數。
? ?這意味著,即使你沒有顯式定義自定義類型的構造函數,編譯器也會自動調用默認構造函數來? ? ? ?初始化這些成員變量。
例子:
class Time
{
public:Time() {// Time類的無參構造函數std::cout << "Time()" << std::endl;_hour = 0;_minute = 0;_second = 0;}private:int _hour;int _minute;int _second;
};class Date
{
public:Date()// 初始化內置類型成員變量{this->_year = 2024;this->_month = 7;this->_day = 5;}void Print(){std::cout << _year << "-" << _month << "-" << _day << std::endl;}private:int _year; // 內置類型int _month; // 內置類型int _day; // 內置類型Time _t; // 自定義類型
};int main()
{Date d; // 調用無參構造函數d.Print();return 0;
}
輸出:
我們來說一下它的執行順序:首先是執行主函數main當執行到?Date d; 時編譯器就會先去調用自定義函數Time_t;然后等它全部初始化完成 再去調用無參數構造并且初始化里面的內置類型。
那我們這是顯式定義自定義類型的構造函數并且給成員變量賦值了,所以就不會出現隨機值,如果想要顯式定義自定義類型的構造函數并且不想要隨機值那該怎么辦呢?這時候C++11 中針對內置類型成員不初始化的缺陷,又打了補丁,即:內置類型成員變量在類中聲明時可以給默認值。
例子:
class Date
{
public:void Print(){std::cout << _year << "-" << _month << "-" << _day << std::endl;}private:int _year = 2024; // 內置類型int _month = 2; // 內置類型int _day = 1; // 內置類型
};
輸出:
2.6:默認構造函數
在C++中,默認構造函數是指在創建對象時不需要提供任何參數的構造函數。默認構造函數可以分為兩種:
- 默認構造函數:一個類只能有一個真正的默認構造函數(不需要參數)。
- 無參構造函數和全缺省參數構造函數:
- 如果參數不同,它們會重載,編譯器不會報錯。
- 如果參數相同(即都沒有參數),它們就相當于有兩個默認構造函數,這時編譯器會報錯,因為無法區分調用哪個構造函數。
關鍵點
無參構造函數:沒有參數的構造函數。 全缺省參數構造函數:所有參數都有默認值的構造函數。 重載:當構造函數的參數列表不同,它們可以共存且不會沖突。
?
例子:
class Date
{
public:// 無參構造函數Date() {_year = 2024;_month = 7;_day = 2;}// 全缺省參數構造函數Date(int year = 2023, int month = 1, int day = 1) {_year = year;_month = month;_day = day;}void Print(){std::cout << _year << "-" << _month << "-" << _day << std::endl;}private:int _year;int _month;int _day;
};int main()
{Date d; // 調用無參構造函數d.Print();Date d2(2023, 4, 3); // 調用全缺省參數構造函數d2.Print(); return 0;
}
輸出:
之所以會報錯是因為全缺省參數構造函數和無參數構造函數它們都有自己的默認值,當執行到Date d; 時它并不知道到底要調用哪一個所以就會報錯,那怎么更改呢?只需要把全缺省參數的默認值給去掉就行了,這樣編譯器就不會迷糊到底要調用哪一個了