作者:gzshun. 原創作品,轉載請標明出處!
來源:http://blog.csdn.net/gzshun
從概念上講,可以認為建構函式分兩個階段進行:
1.初始化階段;
2.普通的計算階段。(計算階段由建構函式函數體中的所有語句組成)
不管成員是否在建構函式初始化列表中顯示初始化,類類型的資料成員總是在初始化階段初始化。初始化發生在計算階段的開始之前。
建議:使用建構函式初始化列表
註:必須對任何const或參考型別成員以及沒有預設建構函式的類類型的任何成員使用初始化式。
一般使用建構函式初始化列表,可以避免發生編譯錯誤。
講解:沒有預設建構函式的類?是什麼意思?
在大部分編譯器中,聲明一個類,若類沒有顯示的聲明和定義建構函式,那麼編譯器就會在編譯階段產生一個預設建構函式。如果使用者在該類中聲明了一個建構函式,那麼編譯器就不會產生預設建構函式,而是使用了使用者自己定義的建構函式,為了避免編譯錯誤,最好使用建構函式初始化列表對該類的對象進行初始化。
-----摘自於《C++ Primer 中文版 第4版》
類成員的初始化包括類對象成員與類資料成員的初始化。初始化比較關鍵的是建構函式的初始化列表,在建構函式中成員初始化列表中也需要次序的。只有建構函式才能有成員初始化的效果,普通的成員函數沒有這功能,比如:
[cpp]
view plaincopy
- void CInit::setXY(int x, int y) : mX(x), mY(y)
- {
- }
這個初始化是錯誤的,setXY並非是建構函式,所以普通成員函數只能通過賦值的形式來設定變數或對象的值。
[cpp]
view plaincopy
- void CInit::setXY(int x, int y)
- {
- mX = x;
- mY = y;
- }
這裡聲明一個類CInit,如下:
[cpp]
view plaincopy
- class CInit
- {
- public:
- CInit(int x, int y);
- void Show() const;
- private:
- int mX;
- int mY;
- };
- void CInit::Show() const
- {
- cout << "mX = " << mX << endl
- << "mY = " << mY << endl;
- }
一.建構函式的初始化列表的基本使用
這是正常的初始化列表的用法
初始化:
[cpp]
view plaincopy
- CInit::CInit(int x, int y) : mX(x), mY(y)
- {
- }
達到的結果相當於
賦值:
[cpp]
view plaincopy
- CInit::CInit(int x, int y)
- {
- mX = x;
- mY = y;
- }
二.成員初始化的次序
在建構函式初始化列表中,成員初始化的次序就是聲明成員的次序。
例子1:張三想先用x初始化mX,再用mX初始化mY
[cpp]
view plaincopy
- CInit::CInit(int x, int y) : mX(x), mY(mX)
- {
- }
- CInit test(2, 3);
- test.Show();
此時的結果是:
[plain]
view plaincopy
- mX = 2
- mY = 2
mX與mY均被成功的初始化。
例子2:李四想先初始化mY,再用mY初始化mX
[cpp]
view plaincopy
- CInit::CInit(int x, int y) : mY(y), mX(mY)
- {
- }
- CInit test(2, 3);
- test.Show();
此時的結果是:
[plain]
view plaincopy
- mX = 2147344384 (不同機器可能不一致)
- mY = 3
從結果可以很明顯的看出,mX沒有被初始化,而mY成功被初始化為3。
從這裡可以看出,建構函式是以變數的聲明順序來執行初始化的動作,所以例子2中,建構函式先初始化mX,但此時mY是未初始化過的,所以導致這種情況。在建構函式的初始化列表中,最好要按照類中成員變數的聲明順序來初始化。
三.在什麼情況下使用建構函式初始化列表?
1.const對象
2.參考型別對象
因為const對象與參考型別對象只能夠初始化,不能賦值,所以必須在初始化列表中進行初始化。
3.類對象(下文說明)
為了說明3.類對象(下文說明),假如有父類是這樣定義的:
[cpp]
view plaincopy
- class CA {
- public:
- CA() { cout << "using ca's constractor/n"; }
- CA(int k) {cout << "using ca's 2nd constractor, k is " << k << endl; m = k;};
- virtual ~CA() { cout << "using ca's disconstractor/n"; }
-
- void output() { cout << "the m is " << m << endl; }
- private:
- int m;
- };
注意A類裡面有一個私人成員m.
假設有一個子類是這樣定義的:
[cpp]
view plaincopy
- class CB : public CA {
- public:
- CB(int k) { m = k; }
- };
顯然是錯誤的,B類不能夠直接存取A類的成員m
這樣定義也是錯誤的:
[c-sharp]
view plaincopy
- class CB : public CA {
- public:
- CB(int k) { __super::CA((int)k); }
- };
這樣實際上是在CB(int k)中構造了一個CA類的臨時變數執行個體,函數執行完之後就沒有了。如果有:
[c-sharp]
view plaincopy
- CB b(2);
執行的結果是:
using ca's constractor
using ca's 2nd constractor, k is 2
using ca's disconstractor
using ca's disconstractor
這說明,先預設的調用CA()構造了一個CB的執行個體,然後又聲明了一個CA (2)的執行個體。
正確的方法是這樣的:
[c-sharp]
view plaincopy
- class CB : public CA {
- public:
- CB(int k) :CA(k) { }
- };
這就是在子類中顯示調用父類的建構函式
四.建構函式中,賦值初始化與初始化列表初始化,哪個效率更高?為什嗎?
先來看一個例子,便可知分曉:
[cpp]
view plaincopy
- #include <iostream>
-
- using namespace std;
-
- class CObj
- {
- public:
- CObj()
- {
- cout << "調用預設建構函式" << endl;
- }
- CObj(const CObj &obj)
- {
- mX = obj.mX;
- cout << "調用複製建構函式" << endl;
- }
- CObj & operator=(const CObj &obj)
- {
- if (&obj != this)
- {
- mX = obj.mX;
- }
- cout << "資料賦值" << endl;
- return *this;
- }
- private:
- int mX;
- };
-
- class CInit
- {
- public:
- CInit(const CObj & obj) : mObj(obj)
- {
- //mObj = obj;
- }
- private:
- CObj mObj;
- };
-
- int main()
- {
- CObj obj;
- cout << endl;
- CInit test(obj);
- return 0;
- }
1.若CInit的建構函式為:
[cpp]
view plaincopy
- CInit(const CObj & obj) : mObj(obj)
- {
- }
執行結果為:[plain]
view plaincopy
- 調用預設建構函式 // CObj obj; 的建構函式的輸出
-
- 調用複製建構函式
在建構函式CInit的初始化列表初始化mObj對象時,調用了複製建構函式1次,總共需要1個行為。
2.若CInit的建構函式為:
[cpp]
view plaincopy
- CInit(const CObj & obj)
- {
- mObj = obj;
- }
執行結果為:
[plain]
view plaincopy
- 調用預設建構函式 // CObj obj; 的建構函式的輸出
-
- 調用預設建構函式
- 資料賦值
在建構函式體中賦值mObj對象時,首先調用預設建構函式,其次是調用operator=賦值運算子,總共需要2個行為。
所以可以得出這麼一個結論:對於使用者自訂的類類型,使用建構函式初始化列表進行初始化的效率,比在建構函式體中賦值初始化的效率更高。對於內建類型,效率是差不多的。
五.建構函式初始化列表的使用例子
[cpp]
view plaincopy
- /*類成員的初始化: 求2點組成的矩形*/
- #include <iostream>
-
- using namespace std;
-
- class CCenterPoint
- {
- public:
- CCenterPoint(int posX, int posY) : mPosX(posX), mPosY(posY)
- {
- }
- void ShowPos() const
- {
- cout << "2點之間的中點座標: (" << mPosX << "," << mPosY << ")" << endl;
- }
- private:
- int mPosX;
- int mPosY;
- };
-
- class CArea
- {
- public:
- CArea(int length, int width) : mLength(length), mWidth(width)
- {
- }
- void ShowArea() const
- {
- cout << "2點組成的矩形面積: " << mLength * mWidth << endl;
- }
- private:
- int mLength;
- int mWidth;
- };
-
- class CRect
- {
- public:
- CRect(int posX1, int posY1, int posX2, int posY2)
- : mPoint((posX1+posY1)/2, (posX2+posY2)/2),
- mArea(posX2-posX1, posY2-posY1)
- {
- }
- void Show() const
- {
- mPoint.ShowPos();
- mArea.ShowArea();
- }
- private:
- CCenterPoint mPoint;
- CArea mArea;
- };
-
- int main()
- {
- CRect rect(10, 100, 20, 200);
- rect.Show();
-
- return 0;
- }
執行結果:
[plain]
view plaincopy
- 2點之間的中點座標: (55,110)
- 2點組成的矩形面積: 1000
Happy Learning!