我們常常在初始化類資料成員的時候,對使用初始化表和在構造函數中十分困惑,這二者有什麼差別呢?我們應該如何選擇呢?
先舉個簡單的例子,看下面的兩段代碼:
代碼A:
-
- inline Account::Account(const char* name,doubl opening_bal)
- ::_name(name),
- _balance(opening_bal)
- {
- _acc_nmbr = get_unique_acct_nmbr();
- }
代碼B:
-
- inline Account::Account(const char* name,doubl opening_bal)
- {
- _name = name;
- _acc_nmbr = get_unique_acct_nmbr();
- _balance = opening_bal;
- }
上述兩段代碼的差別是:
代碼A把對資料成員_name和_balance的初始化放在初始化表中,而代碼B是在構造函數中進行指派操作。
上述兩段代碼的結果是相同的。在構造函數的結束處,三個成員都含有相同的值,差別是成員初始化表隻提供該類資料成員的初始化。
尤其是要注意的是:構造函數中的對成員進行設定值是指派操作,不是初始化操作,其初始化操作已經在初始化表中進行。
我們可以認為構造函數的執行過程分為兩個階段:隐式或顯式初始化階段,以及一般的計算階段。計算階段由構造函數體内的是以語句構成。
初始化階段可以是顯式的或是隐式的,取決于是否存在成員初始化表。隐式初始化階段按照聲明的順序依次調用是以基類的預設構造函數,然後是所有成員類對象的預設構造函數。
例如如下代碼:
-
- class Account{
- public:
- private:
- unsigned int _acct_nmbr;
- double _balance;
- string _name;
- };
-
- inline Account::Account()
- {
- _name = "";
- _balance = 0.0;
- _acc_nmbr = 0;
- }
則初始化階段是隐式的。在構造函數體執行之前,先調用與_name相關聯的預設string構造函數,這意味着把空串指派給_name是不必要的。
對于類對象,在初始化和指派直接的差別是巨大的。成員類對象應該總是在成員初始化表中被初始化,而不是在構造函數體内被指派。預設Account構造函數更正确的實作如下:
-
- inline Account::Account()
- :_name(string())
- {
- _balance = 0.0;
- _acc_nmbr = 0;
- }
去掉了不必要的構造函數體裡面的指派操作。
對于預設構造函數的顯式調用也是不必要的,可以如下實作:
-
- inline Account::Account()
- :_name()
- {
- _balance = 0.0;
- _acc_nmbr = 0;
- }
對于内置類型的資料成員,用成員初始化表和在構造函數體内初始化是相同的嗎?不。
除了兩個例外,兩個在結果和性能上都是等價的。更受歡迎的是用成員初始化表。
兩個例外是任何類型的const和引用資料成員。因為他們必須在成員初始化表中被初始化,否則會産生編譯時刻錯誤。