成員初始化列表的概要介紹

2021-06-25 13:33:49 字數 3691 閱讀 1495

一、   成員初始化列表的位置。

成員初始化列表的位置位於建構函式的函式體和參數列之間。建構函式初始化列表以乙個冒號開始,接著是以逗號分隔的資料成員列表,每個資料成員後面跟乙個放在括號中的初始化式,初始化式可以是表示式、派生類建構函式的形參還有其餘常量。

通過成員初始化表,類資料成員可以被顯式初始化。成員初始化表是由逗號分隔的成員/名字實參對。例如下面的雙引數建構函式的實現就使用了成員初始化表。

_name是string 型的成員類物件。

account::account( const char* name, double opening_bal ): _name( name ), _balance( opening_bal ) 

成員初始化表跟在建構函式的原型後,由冒號開頭。成員名是被指定的,後面是括在括號中的初始值,類似於函式呼叫的語法。如果成員是類物件則初始值變成被傳遞給適當的建構函式的實參。該建構函式然後被應用在成員類物件上。在我們的例子中,name被傳遞給應用在_name上的string建構函式。_balance 用引數opening_bal初始化。

account::account( const string& name, double opening_bal ) : _name( name ), _balance( opening_bal ) 

說明:在這種情況下,string 的拷貝建構函式被呼叫。把成員類物件_name 初始化成string 引數name。

二、   使用初始化表和在建構函式內使用資料成員的賦值之間有什麼區別?

(1)account::account( const char *name, double opening_bal ): _name( name ), _balance( opening_bal )

(2)account::account( const char *name, double opening_bal )

這兩種實現有區別嗎?

兩種實現的最終結果是一樣的。在兩個構造函式呼叫的結束處三個成員都含有相同的值。區別是上面的建構函式(使用初始化列表的建構函式)顯式的初始化類的成員;而沒使用初始化列表的建構函式是對類的成員賦值,並沒有進行顯式的初始化。

我們可以認為建構函式的執行過程被分成兩個階段:隱式或顯式初始化階段以及一般的計算階段。計算階段由建構函式體內的所有語句構成,在計算階段中資料成員的設定被認為是賦值而不是初始化。沒有清楚地認識到這個區別是程式錯誤和低效的常見源泉。 

初始化階段可以是顯式的或隱式的取決於是否存在成員初始化表。隱式初始化階段按照宣告的順序依次呼叫所有基類的預設建構函式然後是所有成員類物件的預設建構函式。

如:

account::account() 

則初始化階段是隱式的。在建構函式體被執行之前先呼叫與_name相關聯的預設string建構函式。這意味著把空串賦給_name的賦值操作是沒有必要的。對於類物件,在初始化和賦值之間的區別是巨大的。成員類物件應該總是在成員初始化表中被初始化而不是在建構函式體內被賦值。 

預設account建構函式更正確的實現如下:

account::account() : _name( string() ) 

它之所以更正確,是因為我們已經去掉了在建構函式體內不必要的對_name 的賦值。但是對於預設建構函式的顯式呼叫也是不必要的。下面是更緊湊但卻等價的實現:

account::account() 

剩下的問題是:對於兩個被宣告為內建型別的資料成員其初始化情況如何?例如用成員初始化表和在建構函式體內初始化_balance是否等價?回答是不。對於非類資料成員的初始化或賦值除了兩個例外,兩者在結果和效能上都是等價的。即更受歡迎的實現是用成員初始化表:

// 更受歡迎的初始化風格 

account:: account(): _balanae( 0.0 ), _acct_nmbr( 0 )

兩個例外是:指任何型別的const 和引用資料成員。const 和引用資料成員也必須是在成員初始化表中被初始化,否則就會產生編譯時刻錯誤。

例如下列建構函式的實現將導致編譯 :

class constref 

;

constref:: constref( int ii ) 

當建構函式體開始執行時,所有const和引用的初始化必須都已經發生。只有將它們在成員初始化表中指定,這才有可能。正確的實現如下:

constref::constref( int ii ):ci( ii ), ri( i ) 

每個成員在成員初始化表中只能出現一次,初始化的順序不是由名字在初始化表中的順序決定而是由成員在類中被宣告的順序決定的。

例:

class account 

;

下面是該類的預設建構函式:

account::account(): _name( string() ), _balance( 0.0 ), _acct_nmbr( 0 ) {}

的初始化順序為acct_nmbr ,_balance 然後是_name 。(由類體內宣告的次序決定的。)但是在初始化表中出現或者在被隱式初始化的成員類物件中的成員,總是在建構函式體內成員的賦值之前被初始化。例如:

account::account( const char *name, double bal ) : _name( name ), _balance( bal )

初始化的順序是_balance , _name 然後是_acct_nmbr。 

(為什麼?因為_balance 在類體內的宣告在 _name之前,_acct_nmbr的宣告雖然在他們之前,但是因為他沒有出現在成員初始化表中,所以。。)

由於這種實際的初始化順序與初始化表內的順序之間的明顯不一致有可能導致以下難於發現的錯誤。當用乙個類成員初始化另乙個時:

class x 

// ... 

};

儘管看起來j 好像是用val 初始化的,而且發生在它被用來初始化i 之前,但實際上是i 先被初始化的。因此它是用乙個還沒有被初始化的j 初始化的。

我們的建議是把用乙個成員對另乙個成員進行初始化的**放到建構函式體內。x::x( int val ) : i( val )

成員初始化列表

類物件的構造順序是這樣的 1.分配記憶體,呼叫建構函式時,隱式 顯示的初始化各資料成員 初始化階段可以是顯式的或隱式的,取決於是否存在成員初始化表。隱式初始化階段按照宣告的順序依次呼叫所有基類的預設建構函式,然後是所有成員類物件的預設建構函式。2.進入建構函式後在建構函式中執行一般計算 計算階段由建...

成員初始化列表

任乙個物件的建立都要呼叫建構函式,而在建構函式中一般要給物件屬性賦值。成員初始化列表 member initialize list 是建構函式中特有的語法,用以簡化對物件屬性的賦值。其用法如下 class myclass 以上 相當於 class myclass 用成員初始化列表的語法來表述很顯然 ...

成員初始化列表

從概念上講,可以認為建構函式分兩個階段執行 1 初始化階段 2 普通的計算階段。計算階段由建構函式函式體中的所有語句組成。不管成員是否在建構函式初始化列表中顯式初始化,類型別的資料成員總是在初始化階段初始化。初始化發生在計算階段開始之前。在建構函式初始化列表中沒有顯式提及的每個成員,使用與初始化變數...