C++ 建構函式;賦值函式(=);解構函式
一.建構函式
類通過一個或幾個成員函式來控制其物件的初始化過程,這些函式成為建構函式。建構函式的任務是初始化類的資料成員,只要類的物件被建立,就會執行建構函式。建構函式與類同名且沒有返回值。
①預設建構函式
預設建構函式是沒有引數的建構函式,當類沒有定義任何建構函式時,編譯器會幫我們合成一個預設建構函式,如:
class A
{
private:
int x ;
double *p;
B y;
};
當我們寫下:
A a;
編譯正常通過,因為有合成的預設建構函式,但是合成的預設建構函式不能對內建型別以及指標、陣列進行初始化,所以上面 x,p是未定義的,而y只有在有預設建構函式時才能被A的合成預設建構函式初始化,否則不能通過編譯
當類有定義任意建構函式時,不再會有預設的建構函式,除非我們在定義一個
class A
{
public:
A(int n): x(n) { }
private:
int x ;
double *p;
B y;
};
A a;//不能通過編譯
需改成:
class A
{
public:
A() { }
A(int n): x(n) { }
private:
int x ;
double *p;
B y;
};
拷貝建構函式只有一個引數同時是本型別的一個引用變數,如:
A(const A &b) { }
在我們未定義拷貝建構函式時,編譯器也會幫我們產生一個預設的拷貝建構函式,進行位拷貝,這在有指標的情況下是不適用的,如:
A a1;
A a2(a1);
這時a2.p == a1.p; 這顯然不是我們所希望的,所以需要我們自己定義從而為p分配記憶體
當我們不希望拷貝現象存在,可將拷貝建構函式定義為私有的
③建構函式的初始化表
建構函式有個特殊的初始化方式叫“初始化表示式表”(簡稱初始化表)。初始化表位於函式引數表之後,卻在函式體{}之前。這說明該表裡的初始化工作發生在函式體內的任何程式碼被執行之前。
以下是建構函式初始化表的使用規則和注意點:
1)如果類存在繼承關係,派生類必須在其初始化表裡呼叫基類的建構函式。
2)類的const 常量只能在初始化表裡被初始化,因為它不能在函式體內用賦值的方式來初始化。
3)成員物件初始化的次序完全不受它們在初始化表中次序的影響,只由成員物件在類中宣告的次序決定。這是因為類的宣告是唯一的,而類的建構函式可以有多個,因此會有多個不同次序的初始化表。如果成員物件按照初始化表的次序進行構造,這將導致解構函式無法得到唯一的逆序。
2. 賦值函式(=)
賦值函式為運算子過載,寫法如下
A &operator=(const A &b);//賦值函式
賦值函式與拷貝建構函式一樣,在未定義時編譯器也會預設生成,也是位拷貝的方式;
拷貝建構函式是在物件被建立時呼叫的,而賦值函式只能被已經存在了的物件呼叫:
A a1;
A a2(a1);//拷貝建構函式
A a3 = a1;//拷貝建構函式
a3 = a2;//賦值函式
3.解構函式
解構函式主要任務是銷燬物件的成員變數,成員變數按照初始化的逆序銷燬,函式形式如下:
~A ( ) { }
編譯器也會幫我們合成預設解構函式,同樣對有指標成員的類不適用
4.建構函式和解構函式可以是虛擬函式嗎
建構函式不能是虛擬函式,因為虛擬函式需要 vtable ,而vtable只有在只有在建構函式呼叫後才生成,這就產生矛盾了。
解構函式可以是虛擬函式,當類有派生類時,解構函式最好是虛擬函式,這樣才能正確釋放資源。