1 class MyCppClass
2 {
3 }
一旦我們為一個類編寫了default constructor,那麼編譯器也就不會為其默認生成default constructor,對於其他幾個函數也一樣。對於編譯器默認生成的constructor來說,它會以一定規則對每一個數據成員進行初始化。考慮到成員初始化的重要性,在編寫自己的constructor時就需要嚴謹認真了,特別是在類的派生與繼承情況下這點顯得尤為重要。對於copy constructor和assignment operator的運用場景,這裡不得不多說一點,見如下代碼:
1 #include <iostream>
2
3 using std::cout;
4 using std::endl;
5
6 class MyCppClass
7 {
8 public:
9 MyCppClass()
10 {
11 std::cout <<"In Default Constructor!" <<std::endl;
12 }
13
14 MyCppClass(const MyCppClass& rhs)
15 {
16 std::cout <<"In Copy Constructor!" <<std::endl;
17 }
18
19 MyCppClass& operator= (const MyCppClass& rhs)
20 {
21 std::cout <<"In Copy Assignment Operator!" <<std::endl;
22
23 return *this;
24 }
25 };
26
27 int main()
28 {
29 MyCppClass testClass1; // default constructor
30 MyCppClass testClass2(testClass1); // copy constructor
31 testClass1 = testClass2; // copy assignment operator
32
33 MyCppClass testClass3 = testClass1; // copy constructor
34
35 return 0;
36 }
執行結果:
1 // 數據成員類型為內置類型
2 class MyCppClass
3 {
4 public:
5 // 賦值操作進行成員初始化
6 MyCppClass
7 {
8 counter = 0;
9 }
10
11 // 初始化列表進行成員初始化
12 MyCppClass : counter(0)
13 {
14 }
15
16 private:
17 int counter;
18 }
當類的數據成員類型為內置類型時,上面兩種初始化方式的效果一樣。當數據成員的類型同樣也為一個類時,初始化的過程就會有不一樣的地方了,比如:
1 // 數據成員類型為自定義類型:一個類
2 class MyCppClass
3 {
4 public:
5 // 賦值操作進行成員初始化
6 MyCppClass(string name)
7 {
8 counter = 0;
9 theName = name;
10 }
11
12 // 初始化列表進行成員初始化
13 MyCppClass : counter(0), theName(name)
14 {
15 }
16
17 private:
18 int counter;
19 string theName;
20 }
在構造函數體內的theName = name這條語句,theName先會調用string的default constructor進行初始化,之後再調用copy assignment opertor進行拷貝賦值。而對於初始化列表來說,直接通過copy constructor進行初始化。明顯起見,可以通過如下的代碼進行測試。
1
2 #include <iostream>
3 #include <string>
4
5 class SubClass
6 {
7 public:
8 SubClass()
9 {
10 std::cout <<" In SubClass Default Constructor!" <<std::endl;
11 }
12
13 SubClass(const SubClass& rhs)
14 {
15 std::cout <<" In SubClass Copy Constructor!" <<std::endl;
16 }
17
18 SubClass& operator= (const SubClass& rhs)
19 {
20 std::cout <<" In SubClass Copy Assignment Operator!" <<std::endl;
21
22 return *this;
23 }
24 };
25
26 class BaseClass
27 {
28 public:
29 BaseClass(const SubClass &rhs)
30 {
31 counter = 0;
32 theBrother = rhs;
33 std::cout <<" In BaseClass Default Constructor!" <<std::endl;
34 }
35
36 BaseClass(const SubClass &rhs, int cnt):theBrother(rhs),counter(cnt)
37 {
38 std::cout <<" In BaseClass Default Constructor!" <<std::endl;
39 }
40
41 BaseClass(const BaseClass& rhs)
42 {
43 std::cout <<" In BaseClass Copy Constructor!" <<std::endl;
44 }
45
46 BaseClass& operator= (const BaseClass& rhs)
47 {
48 std::cout <<" In BaseClass Copy Assignment Operator!" <<std::endl;
49
50 return *this;
51 }
52 private:
53 int counter;
54 SubClass theBrother;
55 };
56
57 int main()
58 {
59 SubClass subClass;
60
61 std::cout <<"\nNo Member Initialization List: " <<std::endl;
62 BaseClass BaseClass1(SubClass);
63
64 std::cout <<"\nMember Initialization List: " <<std::endl;
65 BaseClass BaseClass2(SubClass, 1);
66
67 return 0;
68 }
執行結果:
1 int x = 0; // 顯示初始化x 2 SubClass subClass; // 依賴SubClass的default constructor進行初始化
上面的名詞“缺省初始化”描述的就是當內置類型或者自定義類型的數據沒有進行顯示初始化時的一種初始化狀態,而“隱式初始化”描述的是在該狀態下面進行的具體操作方式,比如對於內置類型來說,缺省初始化狀態下進行的隱式初始化實際上是未定義的,而自定義類型的隱式初始化則依賴於其constructor。
前面提到過C++不保證內置類型的初始化,但是當內置類型在作為一個類的成員時,在某些特定的條件下該內置類型的成員會被編譯器主動進行初始化,對於這個過程也就是所謂的數值初始化。在《Accelerated C++》當中列出了如下的幾種情況: 測試如下: 1 #include <iostream>
2 #include <vector>
3 #include <map>
4 #include <string>
5
6 using std::cout;
7 using std::endl;
8 using std::vector;
9 using std::map;
10 using std::string;
11
12 class NumbericInitTestClass
13 {
14 public:
15 void PrintCounter()
16 {
17 cout <<"counter = " <<counter <<endl;
18 }
19 private:
20 int counter;
21 };
22
23
24 int main()
25 {
26 NumbericInitTestClass tnc;
27 tnc.PrintCounter();
28
29 map<string, int> mapTest;
30 cout <<mapTest["me"] <<endl;
31
32 vector<NumbericInitTestClass> vecNumbericTestClass(1);
33 vecNumbericTestClass[0].PrintCounter();
34
35 return 0;
36 }
對於沒有進行初始化的內置類型,是一個未定義的值2009095316,而對於2, 3種情況來說,均被初始化為0,對於第1種情況我還沒有想到合適的場景。