c++构造函数成员初始化中赋值和初始化列表两种方式的区别
先總結下:
由于類成員初始化總在構造函數執行之前
1)從必要性:
? ? ?a. 成員是類或結構,且構造函數帶參數:成員初始化時無法調用缺省(無參)構造函數
? ? ?b. 成員是常量或引用:成員無法賦值,只能被初始化
2)從效率上:
? ? 如果在類構造函數里賦值:在成員初始化時會調用一次其默認的構造函數,在類構造函數里又會調用一次成員的構造函數再賦值
? ? 如果在類構造函數使用初始化列表:僅在初始化列表里調用一次成員的構造函數并賦值
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
以下內容參考自?c++中構造函數初始化的方法以及主要區別
----------------------
一、我的問題是關于初始化C++類成員的。我見過許多這樣的代碼:?
[cpp] view plaincopy
[cpp] view plaincopy
我的一些程序員朋友說第二種方法比較好,但他們都不知道為什么是這樣。你能告訴我這兩種類成員初始化方法的區別嗎??
回答?
從技術上說,你的程序員朋友是對的,但是在大多數情況下,兩者實際上沒有區別。有兩個原因使得我們選擇第二種語法,它被稱為成員初始化列表:一個原因是必 須的,另一個只是出于效率考慮。?
讓我們先看一下第一個原因——必要性。設想你有一個類成員,它本身是一個類或者結構,而且只有一個帶一個參數的構造函數。?
[cpp] view plaincopy
因為Cmember有一個顯式聲明的構造函數,編譯器不產生一個缺省構造函數(不帶參數),所以沒有一個整數就無法創建Cmember的一個實例。?
[cpp] view plaincopy
如果Cmember是另一個類的成員,你怎樣初始化它呢?你必須使用成員初始化列表。?
[cpp] view plaincopy
沒有其它辦法將參數傳遞給m_member,如果成員是一個常量對象或者引用也是一樣。根據C++的規則,常量對象和引用不能被賦值,它們只能被初始化。?
第二個原因是出于效率考慮,當成員類具有一個缺省的構造函數和一個賦值操作符時。MFC的Cstring提供了一個完美的例子。假定你有一個類 CmyClass具有一個Cstring類型的成員m_str,你想把它初始化為 "yada yada. "。你有兩種選擇:?
[cpp] view plaincopy
在 它們之間有什么不同嗎?是的。編譯器總是確保所有成員對象在構造函數體執行之前初始化,因此在第一個例子中編譯的代碼將調用CString:: Cstring來初始化m_str,這在控制到達賦值語句前完成。在第二個例子中編譯器產生一個對CString:: CString(LPCTSTR)的調用并將 "yada yada "傳遞給這個函數。結果是在第一個例子中調用了兩個Cstring函數(構造函數和賦值操作符),而在第二個例子中只調用了一個函數。在 Cstring的例子里這是無所謂的,因為缺省構造函數是內聯的,Cstring只是在需要時為字符串分配內存(即,當你實際賦值時)。但是,一般而言, 重復的函數調用是浪費資源的,尤其是當構造函數和賦值操作符分配內存的時候。在一些大的類里面,你可能擁有一個構造函數和一個賦值操作符都要調用同一個負 責分配大量內存空間的Init函數。在這種情況下,你必須使用初始化列表,以避免不要的分配兩次內存。在內部類型如ints或者longs或者其它沒有構 造函數的類型下,在初始化列表和在構造函數體內賦值這兩種方法沒有性能上的差別。不管用那一種方法,都只會有一次賦值發生。有些程序員說你應該總是用初始 化列表以保持良好習慣,但我從沒有發現根據需要在這兩種方法之間轉換有什么困難。在編程風格上,我傾向于在主體中使用賦值,因為有更多的空間用來格式化和 添加注釋,你可以寫出這樣的語句:x=y=z=0;?
或者memset(this,0,sizeof(this));?
注意第二個片斷絕對是非面向對象的。?
當我考慮初始化列表的問題時,有一個奇怪的特性我應該警告你,它是關于C++初始化類成員的,它們是按照聲 明的順序初始化的,而不是按照出現在初始化列表中的順序。?
[cpp] view plaincopy
你 可能以為上面的代碼將會首先做m_y=I,然后做m_x=m_y,最后它們有相同的值。但是編譯器先初始化m_x,然后是m_y,,因為它們是按這樣的順 序聲明的。結果是m_x將有一個不可預測的值。我的例子設計來說明這一點,然而這種bug會更加自然的出現。有兩種方法避免它,一個是總是按照你希望它們 被初始化的順序聲明成員,第二個是,如果你決定使用初始化列表,總是按照它們聲明的順序羅列這些成員。這將有助于消除混淆。
二、很多的人對中構造函數寢初始化很多的困惑,對冒號后初始化不是太明白,總搞不清楚它們之間的區別,我想把我對這個問題的理解和看法和大家討論討 論。?
在程序中定義變量并初始化的機制中,有兩種形式,一個是我們傳統的初始化的形式,即賦值運算符賦值,還有一種是括號賦值,如:?
int a=10;?
char b='r';\\賦值運算符賦值?
int a(10);\?
char b('r');\\括號賦值?
以上定義并初始化的形式是正確的,可以通過編譯,但括號賦值只能在變量定義并初始化中,不能用在變量定義后再賦值,這是和賦值運算符賦值的不同之處,如:?
(1)?
int a; \\先定義一個變量?
......?
a=10; \\根據需要賦值?
(2)?
int b; \\先定義一個變量?
......?
b(10); \\和(1)一樣根據需要賦值?
(1)是可以用通過編譯,定義一個變量a但并沒有初始化,在需要變量a的時候,通過賦值運算符把10賦給a,而在(2)中,是通過括號把10賦值給b,但 編譯系統認為?
這是一個函數的調用,函數名為b,10為實際參數,所以編譯錯誤。因此,括號賦值只用在定義變量并初始化中。
現在我們來看構造函數中冒號初始化和函數初始化的問題,類構造函數的作用是創建一個類的對象時,調用它來構造這個類對象的數據成員,一要給出此數據成員分 配內存空間,二是要給函數數據成員初始化,構造數據成員是按數據成員在類中聲明的順序進行構造。?
冒號初始化與函數體初始化的區別在于:?
冒號初始化是給數據成員分配內存空間時就進行初始化,就是說分配一個數據成員只要冒號后有此數據成員的賦值表達式(此表達式必須是括號賦值表達式),那么 分配了內存空間后在進入函數體之前給數據成員賦值,就是說初始化這個數據成員此時函數體還未執行。?
對于在函數中初始化,是在所有的數據成員被分配內存空間后才進行的。?
這樣是有好處的,有的數據成員需要在構造函數調入之后函數體執行之前就進行初始化如引用數據成員,常量數據成員和對象數據成員,看下面的一段程序:?
[cpp] view plaincopy
在Student類中有兩個數據成員,一個是常量數據成員,一個是引用數據成員,并且在構造函數中初始化了這兩個數據成員,但是這并不能通過編譯,因為常 量初始化時必須賦值,它的值是不能再改變的,與常量一樣引用初始化也需要賦值,定義了引用后,它就和引用的目標維系在了一起,也是不能再被賦值的。所以C?
++":"后初始化的機制,使引用和常量數據成員變為可能的,Student類的構造函數應為:?
student ::student(int i,int j):a(i),b(j){}?
在下面的程序:?
[cpp] view plaincopy
在上面的程序中通不過編譯,編譯系統會告訴你teacher這個類對象缺默認構造函數,因為在teach 類中沒有定義默認的構造函數。那么帶參數的構造函數怎么進行構造呢?通過我們前面提到的冒號賦值。那它的構造函數應該是:?
[cpp] view plaincopy
就是說在沒有默認構造函數的時候,如果一個類對象是另一個類的數據成員,那么初始化這個數 據成員,就應該放到冒號后面。這樣可以帶參數。在類的定義中,如:?
protected;?
char name[30];?
teach teacher?
類對象是不能帶參數的,因為它只是聲明。
原文轉載至點擊打開鏈接
總結
以上是生活随笔為你收集整理的c++构造函数成员初始化中赋值和初始化列表两种方式的区别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python打代码运行图形_利用aard
- 下一篇: C++类中成员变量的初始化有两种方式