C++各種建構函式的寫法
建構函式 ,是一種特殊的方法 。主要用來在建立物件時初始化物件, 即為物件成員變數賦初始值,總與new運算子一起使用在建立物件的語句中 。特別的一個類可以有多個建構函式 ,可根據其引數個數的不同或引數型別的不同來區分它們 即建構函式的過載。(摘自百度百科 建構函式 )。
一、最基本的建構函式
1 class Base
2{
3 public:
4 Base(int var) : m_Var(var)
5 {
6 }
7 private:
8 int m_Var;
9 };
以上建構函式的執行過程:
1)傳參 2)給類資料成員開闢空間 3)執行冒號語法給資料成員初始化 4)執行建構函式括號裡面的內容
這裡需要說明的是:冒號語法後面的內容相當於int a = 10;(初始化),而建構函式括號裡面則是相當於是int a; a = 10;(賦初值)
二、拷貝建構函式
1 class Base
2 {
3 public:
4 Base(int var) : m_Var(var)
5 {
6 }
7 //拷貝建構函式
8 Base(Base &ref) : m_Var(ref.m_Var)
9 {
10 }
11 private:
12 int m_Var;
13 };
為什麼拷貝建構函式的引數只能用引用呢?
這就要從拷貝建構函式式數碼時候觸發開始說起了,以下幾種情況都會自動呼叫拷貝建構函式:
1)用一個已有的物件初始化一個新物件的時候
2)將一個物件以值傳遞的方式傳給形參的時候
3)函式返回一個物件的時候
所以當一個物件以傳遞值的方式傳一個函式的時候,拷貝建構函式自動的被呼叫來生成函式中的物件。如果一個物件是被傳入自己的拷貝建構函式,它的拷貝建構函式將會被呼叫來拷貝這個物件這樣複製才可以傳入它自己的拷貝建構函式,這會導致無限迴圈直至棧溢位除了當物件傳入函式的時候被隱式呼叫以外,拷貝建構函式在物件被函式返回的時候也同樣的被呼叫。(摘自百度百科 拷貝建構函式 )。
拷貝建構函式,一般不需要自己編寫,系統預設的拷貝建構函式就能抗住了,但是有些情況需要在構造的時候開闢空間,這時候就需要拷貝構造函數了,如下程式碼是摘自 林銳
1 class String
2 {
3 public:
4 String(const char *str = NULL); // 普通建構函式
5 String(const String &other); // 拷貝建構函式
6 ~ String(void); // 解構函式
7 private:
8 char *m_data; // 用於儲存字串
9 };
10 // String 的解構函式
11 String::~String(void)
12 {
13 delete [] m_data;
14 // 由於m_data 是內部資料型別,也可以寫成 delete m_data;
15 }
16
17 // String 的普通建構函式
18 String::String(const char *str)
19 {
20 if(str==NULL)
21 {
22 m_data = new char[1]; // 若能加 NULL 判斷則更好
23 *m_data = '\0';
24 }
25 else
26 {
27 int length = strlen(str);
28 m_data = new char[length+1]; // 若能加 NULL 判斷則更好
29 strcpy(m_data, str);
30 }
31 }
32 // 拷貝建構函式
33 String::String(const String &other)
34 {
35 int length = strlen(other.m_data);
36 m_data = new char[length+1]; // 若能加 NULL 判斷則更好
37 strcpy(m_data, other.m_data);
38 }
三、普通派生類建構函式的寫法
定義派生類物件的時候,會按如下步驟執行構造操作:
1)傳參 2)根據繼承時的宣告順序構造基類 3)給類資料成員開闢空間 4)執行冒號語法後面的語句 5)執行建構函式函式體語句
1 class Base
2 {
3 public:
4 Base(int b) : m_b(b)
5 {
6 }
7 private:
8 int m_b;
9 };
10
11 class Derived : public Base
12 {
13 public:
14 //普通派生類建構函式的寫法
15 Derived(int b, int d) : Base(b), m_d(d)
16 {
17 }
18 private:
19 int m_d;
20 };
再寫一個多繼承的示例:
1 class Base1
2 {
3 public:
4 Base1(int b1) : m_b1(b1)
5 {
6 }
7 private:
8 int m_b1;
9 };
10
11 class Base2
12 {
13 public:
14 Base2(int b2) : m_b2(b2)
15 {
16 }
17 private:
18 int m_b2;
19 };
20
21 class Derived : public Base1, public Base2
22 {
23 public:
24 Derived(int b1, int b2, int d) : Base1(b1), Base2(b2), m_d(d)
25 { //注意冒號語法後面的順序無所謂,創造基類是按照上面的繼承宣告順序來進行的...
26 }
27 private:
28 int m_d;
29 };
四、含有虛繼承的派生類建構函式的寫法
為何要用到虛繼承?
虛繼承主要是針對多繼承時,出現二義性問題而提出的。比如,如下程式碼就需要用到虛繼承,否則的話Derived類繼承時,Base類就會不明確。
虛繼承建構函式的執行按照如下步驟:
1)傳參 2)建立基類,注意這時候需要顯示建立所有“有參建構函式”的基類,包括直接基類,間接基類。 3)給類資料成員開闢空間 4)執行冒號語法 5)執行建構函式函式體
注:你可能會疑惑,如下程式碼不是將Base間接基類建立了3次嗎?其實不是這樣的,編譯器是這樣處理的,當最遠的派生類Derived建立了基類Base之後,其直接基類建立Base類的語句將會被忽略掉。
1 class Base
2 {
3 public:
4 Base(int b) : m_b(b)
5 {
6 }
7 private:
8 int m_b;
9 };
10
11 class Base1 : virtual public Base
12 {
13 public:
14 Base1(int b, int b1) : Base(b), m_b1(b1)
15 {
16 }
17 private:
18 int m_b1;
19 };
20
21 class Base2 : virtual public Base
22 {
23 public:
24 Base2(int b, int b2) : Base(b), m_b2(b2)
25 {
26 }
27 private:
28 int m_b2;
29 };
30 //虛繼承,避免二義性
31 class Derived : public Base1, public Base2
32 {
33 public:
34 Derived(int b, int b1, int b2, int d) : Base(b), Base1(b, b1), Base2(b, b2), m_d(d)
35 { //注意冒號語法後面的順序無所謂,創造基類是按照上面的繼承宣告順序來進行的...
36 }
37 private:
38 int m_d;
39 };
五、關於虛析構
虛析構一般伴隨著多型而產生,多型主要方式就是用基類的指標或引用指向或引用派生類,而形成多型。
但是這樣就會存在一個問題,當我們析構的時候,由於是基類的指標,就會呼叫的是基類的建構函式,從而造成派生記憶體溢位。為了解決這個問題,引入了虛析構的概念。將基類的建構函式宣告為虛,從而使其在呼叫解構函式的時候能夠準確的呼叫派生類的解構函式。
如下程式碼必須用到虛析構才能準確的析構派生類,並釋放其佔有記憶體。
1 class Base
2 {
3 public:
4 Base(int b) : m_b(b)
5 {
6 }
7 //虛析構,使基類指標能準確的釋放所指向的派生類裡面的內容
8 virtual ~Base()
9 {
10 }
11 private:
12 int m_b;
13 };
14
15 class Derived : public Base
16 {
17 public:
18 Derived(int b, char *pStr) : Base(b)
19 {
20 m_pStr = new char[strlen(pStr)+1];
21 strcpy(m_pStr,pStr);
22 }
23 ~Derived()
24 {
25 delete m_pStr;
26 m_pStr = NULL;
27 }
28 private:
29 char *m_pStr;
30 };
31
32 int main(void)
33{
34 char *pStr = "abcdefg";
35 Base *b = new Derived(1,pStr);
36 delete b;
37
38 return 0;
39 }