天天看點

基礎備忘:類的成員初始化表與構造函數内指派操作

我們常常在初始化類資料成員的時候,對使用初始化表和在構造函數中十分困惑,這二者有什麼差別呢?我們應該如何選擇呢?

先舉個簡單的例子,看下面的兩段代碼:

代碼A:

  1. inline Account::Account(const char* name,doubl opening_bal)  
  2.                         ::_name(name),  
  3.                         _balance(opening_bal)  
  4. {  
  5.     _acc_nmbr = get_unique_acct_nmbr();  
  6. }  

代碼B:

  1. inline Account::Account(const char* name,doubl opening_bal)  
  2. {  
  3.     _name = name;  
  4.     _acc_nmbr = get_unique_acct_nmbr();  
  5.     _balance = opening_bal;  
  6. }  

上述兩段代碼的差別是:

代碼A把對資料成員_name和_balance的初始化放在初始化表中,而代碼B是在構造函數中進行指派操作。

上述兩段代碼的結果是相同的。在構造函數的結束處,三個成員都含有相同的值,差別是成員初始化表隻提供該類資料成員的初始化。

尤其是要注意的是:構造函數中的對成員進行設定值是指派操作,不是初始化操作,其初始化操作已經在初始化表中進行。

我們可以認為構造函數的執行過程分為兩個階段:隐式或顯式初始化階段,以及一般的計算階段。計算階段由構造函數體内的是以語句構成。

初始化階段可以是顯式的或是隐式的,取決于是否存在成員初始化表。隐式初始化階段按照聲明的順序依次調用是以基類的預設構造函數,然後是所有成員類對象的預設構造函數。

例如如下代碼:

  1. class Account{  
  2. public:  
  3. private:  
  4.     unsigned int _acct_nmbr;  
  5.     double _balance;  
  6.     string _name;  
  7. };  

​​​​

  1. inline Account::Account()  
  2. {  
  3.     _name = "";  
  4.     _balance = 0.0;  
  5.     _acc_nmbr = 0;  
  6. }  

則初始化階段是隐式的。在構造函數體執行之前,先調用與_name相關聯的預設string構造函數,這意味着把空串指派給_name是不必要的。

對于類對象,在初始化和指派直接的差別是巨大的。成員類對象應該總是在成員初始化表中被初始化,而不是在構造函數體内被指派。預設Account構造函數更正确的實作如下:

  1. inline Account::Account()  
  2. :_name(string())  
  3. {  
  4.     _balance = 0.0;  
  5.     _acc_nmbr = 0;  
  6. }  

去掉了不必要的構造函數體裡面的指派操作。

對于預設構造函數的顯式調用也是不必要的,可以如下實作:

  1. inline Account::Account()  
  2. :_name()  
  3. {  
  4.     _balance = 0.0;  
  5.     _acc_nmbr = 0;  
  6. }  

對于内置類型的資料成員,用成員初始化表和在構造函數體内初始化是相同的嗎?不。

除了兩個例外,兩個在結果和性能上都是等價的。更受歡迎的是用成員初始化表。

兩個例外是任何類型的const和引用資料成員。因為他們必須在成員初始化表中被初始化,否則會産生編譯時刻錯誤。

繼續閱讀