一、初始化列表定義
初始化列表:以一個冒號開始,接著是一個以逗號分隔的數據成員列表,每個"成員變量"后面跟一個放在括號中的初始值或表達式。
class Date
{
public:Date(int year, int month, int day): _year(year), _month(month)//初始化列表, _day(day){}
private:int _year;int _month;int _day;
};
二、初始化列表內在作用的剖析
不知道大家有沒有想過這樣一個問題,成員函數明明可以在函數內部對成員變量進行賦值,那為什么還要搞出初始化列表這個東西呢?這個時候就需要我們對初始化列表有一個更加深刻的理解了。
我們知道,在一個類被設計出來的時候,它里面定義的成員變量只是變量的聲明,沒有為其分配空間。我們也知道,類定義出一個對象是在主函數中定義的,而創建出來的這個對象其中的成員變量其實是在初始化列表中定義的。那么假設我們創建出來的類的成員變量中含有引用類型或者是被const修飾時(引用類型和const修飾的變量在定義時就必須為其賦初始值),就像下面這個代碼:
?如果我們定義的類內部的成員變量中含有引用類型或者是被const修飾,而在成員函數體內部再對其賦初始值,就相當于_ref和_n兩個變量定義和賦初始值分離了,但我們明確地知道引用類型或者是被const修飾的變量在定義時就必須為其賦初始值,所以編譯器會報未初始化的錯誤。上面圖片中的代碼還可以寫的更明確一點,就相當于下面這種形式:
?五個成員變量全部定義和賦初始值分開,一般的內置類型是支持這種行為的。就像是這樣:
int main()
{int a;a = 10;//可以int& n;n = a;//報錯const int m;m = 10;//報錯return 0;
}
所以正確的方法應該是:類內部的成員變量中含有引用類型或者是被const修飾時,引用類型或者是被const修飾的成員變量必須用初始化列表賦初值(定義時就賦初值)。
class Date
{
private:int _year;int _month;int _day;int& _ref;const int _n;public:Date(int year, int month, int day):_year(),_month(),_day(),_ref(month),_n(1){this->_year = year;this->_month = month;this->_day = day;}
};
三、類中成員變量含有自定義類型的情況
很好理解,自定義也必須使用初始化列表進行初始化,如果自定義類型沒有顯示地調用初始化列表,那么自定義類型就會去調用它的默認構造函數,如果沒有默認構造函數,就會編譯報錯。
#include <iostream>
using namespace std;class A
{
private:int _a;public:A(int a = 0){this->_a = a;}
};class Date
{
private:int _year;int _month;int _day;A aa;int& _ref;const int _n;public:Date(int year, int month, int day):_year() //aa沒有顯示地調用初始化列表,會去調用它的默認構造函數, _month()//剩下的三個成員沒有寫出來定義,但是它也會定義,只是內置類型給的隨機值, _day() //自定義類型會去調用它的默認構造函數,_ref(month),_n(1){this->_year = year;this->_month = month;this->_day = day;}
};//編譯沒有報錯
int main()
{Date d1(2023, 11, 2);return 0;
}
?_a被初始化為了0。還是上面這段代碼,如果將A(int a = 0)改成A(int a),編譯就會報錯,因為沒有合適的默認構造函數。
所以自定義類型在使用初始化列表的時候,建議要顯示地傳參去調用指定的構造函數。
四、初始化列表使用的建議以及小點
class Date
{
private:int _year;int _month;int _day;int* _aa;public:Date(int year, int month, int day):_year(year) //aa沒有顯示地調用初始化列表,會去調用它的默認構造函數, _month(month)//剩下的三個成員沒有寫出來定義,但是它也會定義,只是內置類型給的隨機值, _day(day) //自定義類型會去調用它的默認構造函數,_aa(new int [10]){if (_aa == nullptr){perror("new fail");exit(-1);}}~Date(){delete[] _aa;}
};int main()
{Date d1(2023, 11, 2);return 0;
}
這里有一個小點需要注意:. 成員變量在類中聲明次序就是其在初始化列表中的初始化順序,與其在初始化列表中的先后次序無關。
class A
{
public:A(int a):_a1(a), _a2(_a1){}void Print() {cout << _a1 << " " << _a2 << endl;}
private:int _a2;int _a1;
};int main()
{A aa(1);aa.Print();
}
上面代碼_a2比_a1先聲明,所以_a2先初始化,用_a1初始化_a2,此時_a1為隨機值,所以初始化完_a2為隨機值,再用1初始化_a1,_a1為1。