一、我的問題是關於初始化c++類成員的。我見過許多這樣的**:
csomeclass::csomeclass()
而在別的什麼地方則寫成下面的樣子:
csomeclass::csomeclass() : x(0), y(1)
我的一些程式設計師朋友說第二種方法比較好,但他們都不知道為什麼是這樣。你能告訴我這兩種類成員初始化方法的區別嗎?
回答 從技術上說,你的程式設計師朋友是對的,但是在大多數情況下,兩者實際上沒有區別。有兩個原因使得我們選擇第二種語法,它被稱為成員初始化列表:乙個原因是必 須的,另乙個只是出於效率考慮。
讓我們先看一下第乙個原因——必要性。設想你有乙個類成員,它本身是乙個類或者結構,而且只有乙個帶乙個引數的建構函式。
class cmember
}; 因為cmember有乙個顯式宣告的建構函式,編譯器不產生乙個預設建構函式(不帶引數),所以沒有乙個整數就無法建立cmember的乙個例項。
cmember* pm = new cmember; // error!!
cmember* pm = new cmember(2); // ok
如果cmember是另乙個類的成員,你怎樣初始化它呢?你必須使用成員初始化列表。
class cmyclass ;
//必須使用成員初始化列表
cmyclass::cmyclass() : m_member(2)
沒有其它辦法將引數傳遞給m_member,如果成員是乙個常量物件或者引用也是一樣。根據c++的規則,常量物件和引用不能被賦值,它們只能被初始化。
第二個原因是出於效率考慮,當成員類具有乙個預設的建構函式和乙個賦值操作符時。mfc的cstring提供了乙個完美的例子。假定你有乙個類 cmyclass具有乙個cstring型別的成員m_str,你想把它初始化為 "yada yada. "。你有兩種選擇:
cmyclass::cmyclass()
//使用類成員列表
// and constructor cstring::cstring(lpctstr)
cmyclass::cmyclass() : m_str(_t( "yada yada "))
在 它們之間有什麼不同嗎?是的。編譯器總是確保所有成員物件在建構函式體執行之前初始化,因此在第乙個例子中編譯的**將呼叫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++初始化類成員的,它們是按照聲 明的順序初始化的,而不是按照出現在初始化列表中的順序。
class cmyclass ;
cmyclass::cmyclass(int i) : m_y(i), m_x(m_y)
你 可能以為上面的**將會首先做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為實際引數,所以編譯錯誤。因此,括號賦值只用在定義變數並初始化中。
現在我們來看建構函式中冒號初始化和函式初始化的問題,類建構函式的作用是建立乙個類的物件時,呼叫它來構造這個類物件的資料成員,一要給出此資料成員分 配記憶體空間,二是要給函式資料成員初始化,構造資料成員是按資料成員在類中宣告的順序進行構造。
冒號初始化與函式體初始化的區別在於:
冒號初始化是給資料成員分配記憶體空間時就進行初始化,就是說分配乙個資料成員只要冒號後有此資料成員的賦值表示式(此表示式必須是括號賦值表示式),那麼 分配了記憶體空間後在進入函式體之前給資料成員賦值,就是說初始化這個資料成員此時函式體還未執行。
對於在函式中初始化,是在所有的資料成員被分配記憶體空間後才進行的。
這樣是有好處的,有的資料成員需要在建構函式調入之後函式體執行之前就進行初始化如引用資料成員,常量資料成員和物件資料成員,看下面的一段程式:
class student
student ::student (int i,int j)
在student類中有兩個資料成員,乙個是常量資料成員,乙個是引用資料成員,並且在建構函式中初始化了這兩個資料成員,但是這並不能通過編譯,因為常 量初始化時必須賦值,它的值是不能再改變的,與常量一樣引用初始化也需要賦值,定義了引用後,它就和引用的目標維繫在了一起,也是不能再被賦值的。所以c
++":"後初始化的機制,使引用和常量資料成員變為可能的,student類的建構函式應為:
student ::student(int i,int j):a(i),b(j){}
在下面的程式:
class teach
teach ::teach(char*p,int a)
class student
; student::student(char *p)
在上面的程式中通不過編譯,編譯系統會告訴你teacher這個類物件缺預設建構函式,因為在teach 類中沒有定義預設的建構函式。那麼帶引數的建構函式怎麼進行構造呢?通過我們前面提到的冒號賦值。那它的建構函式應該是:
student::student(char *p,char *pl,int ag):teacher(pl,ag)
就是說在沒有預設建構函式的時候,如果乙個類物件是另乙個類的資料成員,那麼初始化這個數 據成員,就應該放到冒號後面。這樣可以帶引數。在類的定義中,如:
protected;
char name[30];
teach teacher
類物件是不能帶引數的,因為它只是宣告。
所以在c++中就增加了這種機制,這是物件導向程式設計所必須的。不知道我講明白沒有。如不明白請查閱有關資料。
C 中的建構函式 初始化
初始化 兩種形式,或者在函式體裡初始化。class 張三 protected int 飯量 const int 智商 int 小名 你需要知道的知識 int 為引用變數,需要在定義的時候初始化.const為恒量限定符,需要在定義的時候初始化,且const建立之後不允許再次賦值 本 由於上述原因而編譯...
c 中建構函式初始化的方法以及主要區別
一 我的問題是關於初始化c 類成員的。我見過許多這樣的 csomeclass csomeclass 而在別的什麼地方則寫成下面的樣子 csomeclass csomeclass x 0 y 1 我的一些程式設計師朋友說第二種方法比較好,但他們都不知道為什麼是這樣。你能告訴我這兩種類成員初始化方法的區...
(C )建構函式初始化
vc 中的對於類的定義是有很大的不同。例如在.net程式中對於需要初始化的引數可以直接的賦值,而在vc 中對於需要初始化的引數則需要使用不同的方法。例如對於char字元則需要使用strcpy s函式賦值而相反對於布林型別 bool 整數型別 int 則可以使用運算子 對其初始化。1.定義乙個帶有引數...