構造函數
- 成員函數的一種
- 名字與類名相同,可以有參數,不能有傳回值(void也不行)
- 作用是對對象進行初始化,如給成員變量賦初值
- 對象生成時構造函數自動被調用。對象一旦生成,就再也不能在其上執行構造函數
- 一個類可以有多個構造函數
構造函數調用方式:
- 顯式調用:CLASS a=CLASS(參數清單);
- 隐式調用:CLASS a(參數清單);
為什麼需要構造函數:
-
構造函數執行必要的初始化工作,有了構造函數,就不
必專門再寫初始化函數,也不用擔心忘記調用初始化函數。
- 有時對象沒被初始化就使用,會導緻程式出錯。
幾種特殊構造函數:
預設構造函數:
- 即無參數(或者預設類型參數)的構造函數
- 如果定義類時沒寫構造函數,則編譯器生成一個預設的無參數 的構造函數
- 預設構造函數無參數,不做任何操作
- 如果定義了構造函數,則編譯器不生成預設的無參數的構造函數
類型轉換構造函數:
-
通常來說隻有一個參數且不是複制構造函數的構造函數即可認為是類型轉換函數。
也就是說隻有一個參數的構造函數允許使用指派文法來初始化對象和進行指派操作。
- 需要注意的是,使用類型轉換構造函數初始化時不會生成臨時對象,而進行指派操作時編譯系統會自動調用轉換構造函數,建立 一個無名的臨時對象(或臨時變量)。
- 但有時候設計者可能希望單參數構造函數有其他作用,此時為了防止語義混淆不清,可以使用explicit關鍵字防止類構造函數的隐式自動轉換
.例子如下:
class Complex {
public:
double real, imag;
explicit Complex( int i) {//顯式類型轉換構造函數
cout << "IntConstructor called" << endl;
real = i; imag = 0;
}
Complex(double r,double i) {real = r; imag = i; }
};
int main () {
Complex c1(7,8);
Complex c2 = Complex(12);
c1 = 9; // error, 9不能被自動轉換成一個臨時Complex對象
c1 = Complex(9) //ok
cout << c1.real << "," << c1.imag << endl;
return 0;
}
-
類型轉換構造函數将double ,int等其他類型值轉換為對象。那麼對應就有轉換函數可以将對象轉換為其他資料類型。
格式如下:在class内:operator typename()。同樣的也可以用關鍵字explicit來關閉其隐式轉換(c++11)
複制(拷貝)構造函數:
- 即隻有一個參數且為對象的引用的構造函數(A::A(A & )或A::A(const A&),A為某構造類)。
- 如果沒有定義複制構造函數,那麼編譯器生成預設 複制構造函數。預設的複制構造函數完成複制功能。
- 如果定義的自己的複制構造函數, 則預設的複制構造函數不存在。
複制構造函數起作用的三種情況
- 當用一個對象去初始化同類的另一個對象時。
Complex c2(c1);
Complex c2 = c1; //初始化語句,非指派語句
- 如果某函數有一個參數是類 A 的對象,那麼該函數被調用時,類A的複制構造函數将被調用。
void Func(A a1){ }
int main(){
A a2;
Func(a2);
return 0;
}
程式輸出結果為: Copy constructor called
- 如果函數的傳回值是類A的對象時,則函數傳回時,A的複制構造函數被調用:
A Func() {
A b(4);
return b;
}
int main() {
cout << Func().v << endl;
return 0;
}
輸出結果:
Copy constructor called
※注意:對象間指派并不導緻複制構造函數被調用
預設複制構造函數可能出現的問題:
- 複制構造函數是按值進行複制的,當遇到類成員裡有指針變量時,它無法複制指針指向的内容而僅僅是複制了位址,使得兩指針指向同一片記憶體。
- 預設構造函數在其建立的臨時對象消亡時會調用析構函數,當析構函數是設計者建立含靜态變量的類時,如在順序棧類中,析構函數顯然需要将top–,如若不小心調用預設的複制構造函數建立了對象,那麼對象消亡時調用析構函數會使top–即多減了1.
常量引用參數的使用
void fun(CMyclass obj_ ) {
cout << "fun" << endl;
}
這樣的函數,調用時生成形參會引發複制構造函數調用,開銷比較大。
是以可以考慮使用 CMyclass & 引用類型作為參數。
如果希望確定實參的值在函數中不應被改變,那麼可以加上const 關鍵字:
void fun(const CMyclass & obj) {
//函數中任何試圖改變 obj值的語句都将是變成非法
}
析構函數:
- 名字與類名相同,在前面加‘ ~’ , 沒有參數和傳回值,
- 一個類最多隻能有一個析構函數。
-
析構函數對象消亡時即自動被調用。
比如
delete對象
參數對象消亡
臨時對象消亡
- 可以定義析構函數來在對象消亡前做善後工作,比如釋放配置設定的空間等。
- 如果定義類時沒寫析構函數,則編譯器生成預設析構函數。 預設析構函數什麼也不做。
- 如果定義了析構函數,則編譯器不生成預設析構函數。
關于構造函數和析構函數什麼時候被調用:
C++自動提供的成員函數:
- 預設構造函數
- 預設析構函數
- 複制構造函數
- 指派運算符
- 位址運算符
有時候這些預設的定義會與設計的類需要的功能不符,是以需要自己設計預設構造函數,析構函數,複制構造函數以及重載相關的運算符。
(本文參考北大郭炜老師的慕課程式設計與算法三和《C++primer plus 第六版》,如有錯誤還望大佬指出)