構造函數是特殊的成員函數,只要創建類類型的新對象,都要執行構造函數。
構造函數的工作是保證每個對象的數據成員具有合適的初始值。
構造函數的名字和類的名字相同,並且不能指定返回類型,像其他任何函數一樣,它們可以沒有形參,也可以定義多個形參。
1.1 構造函數可以被重載
1.2 實參決定使用哪個構造函數
1.3 構造函數自動執行
只要創建該類型的一個對象,編譯器就運行一個構造函數。
1.4 構造函數不能聲明爲const
創建類類型的const對象時,運行一個普通構造函數來初始化該const對象。
2.1 構造函數初始化式
構造函數還可以包含一個構造函數初始化列表:
B(const string &book):m1(book), m2(0), m3(0.0) { }
構造函數初始化列表以一個冒號開始,接着是一個以逗號分割的數據成員列表,每個數據成員後面跟一個放在圓括號中的初始化式。
構造函數初始化式只在構造函數的定義中而不是聲明中制定。
省略初始化列表並在構造函數的函數體內對數據成員賦值是合法的。例如:
B::B(const string &book) {
m1 = "book",
m2 = 0;
m3 = 0.0;
}
這個構造函數給類B的成員函數賦值,但沒有進行顯示的初始化。不管是否有顯式的初始化式,在執行這個構造函數之前,要先初始化m1,m2,m3。這個構造函數隱式的使用默認的string構造函數來初始化成員變量,所以執行這個構造函數之後,m1,m2,m3被構造函數體中的賦值所覆蓋。
也就是說,定義初始化列表和沒有定義初始化列表的構造函數的區別在於,前者是初始化數據成員,否者是對調用了默認構造函數後的數據成員進行賦值。
沒有默認構造函數的類類型的成員,以及const或引用類型的成員,都必須在構造函數初始化列表中進行初始化。(初始化const或應用類型數據成員的唯一機會是在構造函數初始化列表中。)例如,當成員變量m2的聲明是const int m2;則上面的構造函數是錯誤的,只能用初始化列表。
2.2成員初始化的次序
成員被初始化的次序就是定義成員的次序。例如:
class X {
int i;
int j;
public:
X(int k): j(k), i(j) { }
};
上面的初始化的效果是先用尚未初始化的j來初始化i,接着纔是用初始化j,這樣做通常跟預期的效果是不一樣的,有時是危險的。
3 默認實參與構造函數
4 默認構造函數
4.1 綜合的默認構造函數
只有當一個類沒有定義構造函數時,編譯器纔會自動生成一個默認構造函數。
綜合的默認構造函數初始化成員,具有類類型的成員通過各自的默認構造函數來進行初始化,內置和符合類型的成員,如指針和數組,只對定義在全局作用域的對象才初始化,對定義在局部作用域的內置或複合類型成員不進行初始化。
4.2 類通常應定義一個默認構造函數
4.3 使用默認構造函數
先看一條語句:
B b();
如果我們要聲明用一個默認構造函數初始化的對象,用上面的方法就錯了,編譯器其實認爲這是個函數的聲明,使用默認構造函數正確的聲明方式:
B b;
去掉括號,或者:
B b = B();
4.4 隱式類類型轉換
看以下代碼:
B func(B b) {
return b;
}
void main() {
B temp = func(5);
}
func期待的實參類型是B,但是傳遞的確是int類型,這裏編譯器使用int的構造函數生成一個新的臨時的B對象傳遞給func函數。也就是將5隱式的轉換成了B對象。
通過在構造函數前添加關鍵字explicit可消除這種隱式轉換。
4.5 類成員的顯式初始化
對於沒有定義構造函數並且其全體數據i成員均爲public的類,可以草用與初始化數組元素相同的方式初始化其成員,例如:
struct Data {
int ival;
char *ptr;
}
Data val1 = { 1024, "abcde" }