1、個人問題是關於初始化C++類成員的。我見過許多這樣的代碼:
CSomeClass::CSomeClass()
{
x=0;
y=1;
}
而在別的什麼地方則寫成下面的樣子:
CSomeClass::CSomeClass() : x(0), y(1)
{
}
個人一些程序員朋友說第二種方法比較好,但他們都不知道爲何是這樣。你能告訴我這兩種類成員初始化方法的區別嗎?
回答
從技術上說,你的程序員朋友是對的,可是在大多數狀況下,二者實際上沒有區別。有兩個緣由使得咱們選擇第二種語法,它被稱爲成員初始化列表:一個緣由是必 須的,另外一個只是出於效率考慮。
讓咱們先看一下第一個緣由——必要性。設想你有一個類成員,它自己是一個類或者結構,並且只有一個帶一個參數的構造函數。
class CMember {
public:
CMember(int x) { ... }
};
由於Cmember有一個顯式聲明的構造函數,編譯器不產生一個缺省構造函數(不帶參數),因此沒有一個整數就沒法建立Cmember的一個實例。
CMember* pm = new CMember; // Error!!
CMember* pm = new CMember(2); // OK
若是Cmember是另外一個類的成員,你怎樣初始化它呢?你必須使用成員初始化列表。
class CMyClass {
CMember m_member;
public:
CMyClass();
};
//必須使用成員初始化列表
CMyClass::CMyClass() : m_member(2)
{
...
}
沒有其它辦法將參數傳遞給m_member,若是成員是一個常量對象或者引用也是同樣。根據C++的規則,常量對象和引用不能被賦值,它們只能被初始化。
第二個緣由是出於效率考慮,當成員類具備一個缺省的構造函數和一個賦值操做符時。MFC的Cstring提供了一個完美的例子。假定你有一個類 CmyClass具備一個Cstring類型的成員m_str,你想把它初始化爲 "yada yada. "。你有兩種選擇:
CMyClass::CMyClass() {
// 使用賦值操做符
// CString::operator=(LPCTSTR);
m_str = _T( "yada yada ");
}
//使用類成員列表
// 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(int x, int y);
int m_x;
int m_y;
};
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會更加天然的出現。有兩種方法避免它,一個是老是按照你但願它們 被初始化的順序聲明成員,第二個是,若是你決定使用初始化列表,老是按照它們聲明的順序羅列這些成員。這將有助於消除混淆。程序員
2、不少的人對中構造函數寢初始化不少的困惑,對冒號後初始化不是太明白,總搞不清楚它們之間的區別,我想把我對這個問題的理解和見解和你們討論討 論。
在程序中定義變量並初始化的機制中,有兩種形式,一個是咱們傳統的初始化的形式,即賦值運算符賦值,還有一種是括號賦值,如:
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
{public :
student ()
.
.
.
protected:
const int a;
int &b;函數
}
student ::student (int i,int j)
{
a=i;
b=j;
}
在Student類中有兩個數據成員,一個是常量數據成員,一個是引用數據成員,而且在構造函數中初始化了這兩個數據成員,可是這並不能經過編譯,由於常 量初始化時必須賦值,它的值是不能再改變的,與常量同樣引用初始化也須要賦值,定義了引用後,它就和引用的目標維繫在了一塊兒,也是不能再被賦值的。因此C
++":"後初始化的機制,使引用和常量數據成員變爲可能的,Student類的構造函數應爲:
student ::student(int i,int j):a(i),b(j){}
在下面的程序:
class teach
{
public :
teach(char *p="name",int a=0)
.
.
.
protected:
char name[30];
int age;
}
teach ::teach(char*p,int a)
{
strcopy(name ,p);
age=a;
cout>>name>>endl;
}
class student
{
public:
student (char *p="name");
.
.
.
protected;
char name[30];
teach teacher;
};
student::student(char *p)
{
strcopy(name,p);
cont>>name>>endl;
}
在上面的程序中通不過編譯,編譯系統會告訴你teacher這個類對象缺默認構造函數,由於在teach 類中沒有定義默認的構造函數。那麼帶參數的構造函數怎麼進行構造呢?經過咱們前面提到的冒號賦值。那它的構造函數應該是:
student::student(char *p,char *pl,int ag):teacher(pl,ag)
{
strcopy(name,p);
cont>>name>>endl;
}
就是說在沒有默認構造函數的時候,若是一個類對象是另外一個類的數據成員,那麼初始化這個數 據成員,就應該放到冒號後面。這樣能夠帶參數。在類的定義中,如:
protected;
char name[30];
teach teacher
類對象是不能帶參數的,由於它只是聲明。
因此在C++中就增長了這種機制,這是面向對象編程所必須的。不知道我講明白沒有。如不明白請查閱有關資料。性能