C++中構造函數的寫法

class Circle函數

{性能

public:.net

        Circle(float r);對象

private:blog

        float radius;內存

};資源

Circle::Circle(float r) { radius = r }字符串

一般都是這麼寫的。還有一種寫法,類名::類名(形參表):內嵌對象1(形參表),內嵌對象2(形參表)... { 類的初始化 }。使用初始化列表比使用賦值語句的效率要高。編譯器

上面的構造函數能夠寫成以下形式:string

Circle::Circle(float r):radius(r){}

 

爲何使用初始化列表比使用賦值語句效率高呢?

1:初始化列表」來初始化成員變量,進行的是初始化工做,調用的是構造函數,只須要調用一次拷貝構造函數;

2:在構造函數裏面直接賦值,進行的是賦值操做,調用的是assignment函數,在調用assignment函數以前,還必需調用一次默認構造函數;

3:有些時候必須用到初始化列表,而不能在構造函數裏直接賦值,好比引用成員和const成員,由於它們都不能被賦值,而只能夠被初始化;

 

Effective C++筆記—構造函數賦值VS初始化列表

http://blog.csdn.net/lifeisbetter/article/details/5099085

條款12: 儘可能使用初始化而不要在構造函數裏賦值

條款13: 初始化列表中成員列出的順序和它們在類中聲明的順序相同

 

在寫構造函數時,必須將參數值傳給相應的數據成員。有兩種方法來實現。

第一種方法是使用成員初始化列表:

第二種方法是在構造函數體內賦值:

兩種方法有重大的不一樣。

從純實際應用的角度來看,有些狀況下必須用初始化。特別是類成員有const(由於const成員只能被初始化,不能被賦值)和引用數據成員只能用初始化,不能被賦值。

 

對象的建立分兩步:

1. 數據成員初始化。(參見條款13)

2. 執行被調用構造函數體內的動做。

 

01.template<class t> 

02.class namedptr { 

03.public: 

04.  namedptr(const string& initname, t *initptr); 

05.  ... 

06. 

07.private: 

08.  const string name; 

09.  t * const ptr; 

10.}; 

 

若是沒有爲name指定初始化參數,string的缺省構造函數會被調用。當在namedptr的構造函數裏對name執行賦值時,會對name調用operator=函數。這樣總共有兩次對string的成員函數的調用:一次是缺省構造函數,另外一次是賦值。

相反,若是用一個成員初始化列表來指定name必須用initname來初始化,name就會經過拷貝構造函數以僅一個函數調用的代價被初始化。

換句話說,經過成員初始化列表來進行初始化老是合法的,效率也決不低於在構造函數體內賦值,它只會更高效。另外,它簡化了對類的維護(見條款m32),由於若是一個數據成員之後被修改爲了必須使用成員初始化列表的某種數據類型,那麼,什麼也不用變。

但有一種狀況下,對類的數據成員用賦值比用初始化更合理。這就是當有大量的固定類型的數據成員要在每一個構造函數裏以相同的方式初始化的時候。這時應該將成員初始化列表用一個對普通的初始化函數的調用來代替。

 

例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++的規則,常量對象和引用不能被賦值,它們只能被初始化。

 

例2 效率

當成員類具備一個缺省的構造函數和一個賦值操做符時。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或者其它沒有構造函數的類型下,在初始化列表和在構造函數體內賦值這兩種方法沒有性能上的差異。無論用那一種方法,都只會有一次賦值發生。

 

例3 順序

關於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將有一個不可預測的值。有兩種方法避免它,一個是老是按照你但願它們被初始化的順序聲明成員,第二個是,若是你決定使用初始化列表,老是按照它們聲明的順序羅列這些成員。

相關文章
相關標籤/搜索