2011-08-26 33 views
6

我是C++的新手,並且還有類的全部概念 - 我仍然在閱讀一本書來嘗試學習。這本書我讀說,當你建造一個一流的,我可以做這個默認值分配:具有初始值的類構造

class foo { 
public: 
    foo(char c, int i); 
private: 
    char exampleChar; 
    int exampleInt; 
}; 

foo::foo(char c, int i): 
exampleChar(c), 
exampleInt(i) 
{} 

此代碼(對我來說)看起來非常凌亂,而並不意味着我的規則用於其他語言。 我的問題是,做上述和這個(下面,我個人認爲看起來更清潔)之間有什麼區別?

foo::foo(char c, int i) { 
    exampleChar = c; 
    exampleInt = i; 
} 

樣的東西,我想是:是否有性能/效率問題,如果大規模做 - 或者是完全一樣的?

+1

「此代碼...並不意味着我在其他語言中使用規則」好吧,C++是不是那些其他語言之一。 C++中有很多與「其他語言」不同的東西(可能與其他語言中的其他語言有很多不同之處)。 –

+1

非常類似的問題:http://stackoverflow.com/questions/1598967/benefits-of-initialization-lists,http://stackoverflow.com/questions/926752/why-should-i-prefer-to-use-member -initialization-list,http://stackoverflow.com/questions/4589237/c-initialization-lists,可能更多 –

回答

10

第一種做法: exampleChar(c), exampleInt(i)稱爲初始化列表。

如果你做的第二種方式,這兩個變量是默認構造第一,然後你將它們分配一個值。 (當輸入構造函數的實體時,初始化列表未初始化的任何內容都是默認構造的。)這是浪費時間,因爲無論如何您只是覆蓋這些值。對於像intchar這樣的小類型,這不是什麼大問題,但是當這些成員變量是需要構建大量循環的大類型時,您一定要使用初始化程序列表。

第二種方式不會浪費時間給他們一個默認值,然後覆蓋它 - 它會直接將它們的值設置爲您給它的值(或者如果該成員是對象,則調用正確的構造函數)。

你可以看到我們做這個的意思是:

class MyClass { 
public: 
    int _i; // our data 

    // default constructor 
    MyClass() : _i(0) { cout << "default constructor"; } 

    // constructor that takes an int 
    MyClass(int i) : _i(i) { cout << "int constructor"; } 

    // assignment operator 
    void operator=(int i) { _i = i; cout << "assignment operator"; } 
}; 

class OtherClass { 
public: 
    MyClass c; 

    OtherClass() { 
     c = 54; 
    } 
}; 

OtherClass oc; 

你會看到,

default constructor 
assignment operator 

打印。這是兩個函數調用,對於其他類,這可能會很昂貴。

如果更改的OtherClass構造函數

OtherClass() : c(54) { } 

你會看到,

int constructor 

打印。只有一個電話相比,兩個。這是最有效的方法。

初始化列表也是必須的,當你

  1. 有沒有默認構造函數的類型。您必須在初始化程序列表中調用正確的構造函數。

  2. 必須要給予一定的值(而不是僅僅有permantently默認值

  3. 有一個參考部件const成員。你必須對這些使用初始化列表。

tl; dr:這樣做是因爲它至少一樣快,但從不比其他方式慢,有時甚至更快。

對於內置類型,如intchar,它們實際上根本沒有被構建;他們只是擁有他們曾經有過的任何記憶的價值。

+0

C++不會將缺省值分配給基元。這就是爲什麼使用未賦值的變量會給你一個警告 - 這是未定義的行爲,因爲它保存了恰好在該內存塊中的隨機值。 –

+0

@Yhai好趕上,我忘了提及。添加了一個筆記。 –

1

好了,這是一個典型的常見問題解答問題:http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.6

在你的情況下,使用charint沒有差異。

一般規則:使用初始化列表作爲不可能性,也有極少數情況下,當你喜歡的分配,例如用於提高可讀性:

MyClass::MyClass 
{ 
    a = b = c = d = e = f = 0; 
} 

優於

class MyClass::MyClass : a(0), b(0), c(0), d(0), e(0), f(0) { } 
5

所不同的是編譯器將始終在第一個用戶定義的構造函數語句之前初始化所有成員(按聲明順序)。在charint這兩種都是原始類型的情況下,'初始化'實際上在這裏意味着'不初始化'。但是,如果你有,做一些實際的工作,一個構造函數的成員,此構造將先期稱爲 - 如果你這樣做

foo::foo() { 
    myComplexMember = MyComplexClass(42); 
} 

編譯器也已經調用你的代碼之前MyComplexClass默認構造函數得到調用,這是浪費資源(如果默認ctor不可訪問,則會出現編譯器錯誤)。

通過使用初始化列表,您可以自定義默認初始化並避免無所事事。顯然,這是要走的路。

5

有些事情你可以這樣做,否則你不能這樣做。

  1. 如果其中一個成員沒有默認構造函數。這是您可以在施工中發起成員的唯一方式。 (同樣適用於基類)

  2. 您可以爲const成員指定一個值。

  3. 在構造函數開始運行之前,您可以確保該類的定義狀態。

  4. 如果成員是引用,它需要在初始化列表中初始化。因爲引用是不可改變的,可以在一開始只初始化一次(如常量)

1

如果會員有不平凡的構造函數,下面先默認構造方法的代碼會被調用,則指派將是執行,而在上面的代碼中,它們只會被初始化一次。所以是的,可能會有性能問題。

還有一個實際問題:如果它們是const,引用或沒有默認構造函數,則不能使用下面的版本。

1

這兩個選項之間有一個微妙但重要的區別。你在頂部有什麼叫做成員初始化列表。創建對象時,此列表中的成員將初始化爲您放入括號中的任何內容。

當你在構造函數體分配,該值是第一初始化,然後分配。我將在下面發佈一個簡短的例子。

例1:成員初始化

class foo 
{ 
public: 
    foo(char c, int i); 

private: 
    char exampleChar; 
    int exampleInt; 
    Bar exampleBar; 
}; 

foo::foo(char c, int i): 
    exampleChar(c), 
    exampleInt(i), 
    exampleBar()  //Here, a bar is being default constructed 
{ 
} 

例2:構造函數賦值

class foo 
{ 
public: 
    foo(char c, int i, Bar b); 

private: 
    char exampleChar; 
    int exampleInt; 
    Bar exampleBar; 
}; 

foo::foo(char c, int i, Bar b): 
    //exampleChar(c), 
    //exampleInt(i), 
    //exampleBar() 
{ 
    exampleChar = c; 
    exampleInt = i; 
    exampleBar = someOtherBar; //Here, a bar is being assigned 
} 

這是它變得有趣。請注意,正在分配exampleBar。在幕後,Bar實際上首先被默認構建,即使您沒有指定。此外,如果你的Bar比一個簡單的結構更復雜,你將需要確保實現賦值運算符,以便以這種方式初始化它。此外,爲了從成員初始化列表中初始化Bar從另一個Bar,您必須實現複製構造函數!

例3:在成員初始化使用拷貝構造函數

class foo 
{ 
public: 
    foo(char c, int i, Bar b); 

private: 
    char exampleChar; 
    int exampleInt; 
    Bar exampleBar; 
}; 

foo::foo(char c, int i, Bar b): 
    //exampleChar(c), 
    //exampleInt(i), 
    exampleBar(b)  //Here, a bar is being constructed using the copy constructor of Bar 
{ 
    exampleChar = c; 
    exampleInt = i; 
} 
0

我會養成使用初始化列表的習慣。當有人將char改爲一個首先調用默認構造函數的對象時,它們也不會遇到問題,而且const函數對於const值也是如此!

0
foo::foo(char c, int i):exampleChar(c),exampleInt(i){} 

這種結構被稱爲成員初始化列表在C++中。

您的會員exampleChar初始化爲值c & exampleInti


是什麼初始化之間分配的內部構造的區別? &
有什麼優勢?

初始化使用初始化列表的成員並在構造函數體內爲其指定值之間有區別。

當你通過初始化列表初始化字段時,構造函數將被調用一次。

如果您使用賦值,那麼這些字段將首先使用默認構造函數進行初始化,然後使用實際值重新分配(通過賦值運算符)。

正如你看到有創作&分配在後者的額外開銷,這可能是相當大的用戶定義的類。

對於整數數據類型(您使用它)或POD類成員沒有實際開銷。