2011-08-22 99 views
46

在C++中,可以使用初始化程序列表在構造函數開始運行之前初始化類的字段。例如:爲什麼Java不像C++那樣有初始化器列表?

Foo::Foo(string s, double d, int n) : name(s), weight(d), age(n) { 
    // Empty; already handled! 
} 

我很好奇爲什麼Java沒有類似的功能。據核心的Java:第1卷

C++使用這種特殊的語法來調用構造函數領域。在Java中,不需要它,因爲對象沒有子對象,只有指向其他對象的指針。

這裏是我的問題:「因爲對象沒有子對象」

  1. 他們是什麼意思由我不明白什麼是子對象(我試圖查找它);它們是否意味着一個擴展超類的子類的實例?至於爲什麼Java沒有像C++這樣的初始化器列表,我會假定原因是因爲所有的字段在Java中已經被默認初始化了,並且因爲Java使用關鍵字super來調用super(或者base C++術語) - 構造函數。它是否正確?

回答

86

在C++中,初始化器列表是因爲一些語言功能要麼在Java中不存在必要的或在Java中的工作方式不同:

  1. const:在C++中,你可以定義標記const一個字段不能分配並須在初始化程序列表中進行初始化。 Java確實有final字段,但您可以在構造函數的主體中分配給final字段。在C++中,分配給構造函數中的const字段是非法的。

  2. 參考文獻:在C++中,引用(與指針相對)必須初始化以綁定到某個對象。創建沒有初始化器的引用是非法的。在C++中,您指定的方式與初始化程序列表一致,因爲如果您在未初始化的情況下引用構造函數的主體中的引用,則會使用未初始化的引用。在Java中,對象引用的行爲與C++指針類似,可以在創建後分配給它。否則,他們只是默認爲null

  3. 直接子對象。在C++中,對象可以直接作爲字段包含對象,而Java對象只能將引用包含到這些對象中。也就是說,在C++中,如果聲明一個具有string作爲成員的對象,則該字符串的存儲空間將直接構建到該對象本身的空間中,而在Java中,您只需獲得空間以引用其他一些對象String對象存儲在別處。因此,C++需要提供一種方法讓你給這些子對象初始值,否則它們會保持未初始化。默認情況下,它使用這些類型的默認構造函數,但是如果您想使用其他構造函數或者沒有默認構造函數可用,則初始化程序列表將爲您提供一種繞過此方法的方法。在Java中,您不必擔心這種情況,因爲引用將默認爲null,然後可以將它們分配爲引用您實際希望它們引用的對象。如果你想使用非默認的構造函數,那麼你不需要任何特殊的語法;只需將引用設置爲通過相應的構造函數初始化的新對象即可。

在極少數情況下的Java可能要初始化列表(例如,調用父類的構造函數或給予默認值的字段),這是通過其他兩種語言功能的處理:將super關鍵字調用父類的構造函數,以及Java對象可以在聲明它們的字段時賦予其字段默認值這一事實。由於C++具有多重繼承,只有一個關鍵字super不會明確指向單個基類,而在C++ 11之前,C++不支持類中的默認初始值設定項,並且必須依賴初始值設定項列表。

希望這會有所幫助!

+0

優秀的答案。我對於我的問題的迴應時間非常驚訝。我註冊後我會趕上你。此外,有關C++ 0x的變化鏈接將不勝感激。 –

+0

@ Jesse-很高興幫助!如果你認爲它回答了這個問題,不要忘記接受答案。 :-)我已經在問題中包含了一個C++ 0x更改的鏈接。 – templatetypedef

+1

在晚上一個小時內有19張票... nice :-) –

1

因爲Java不需要它們來允許初始化類型沒有零值的字段。

在C++

class C { 
    D d; 
} 

未經成員初始化爲dD::D()將被稱爲這使得不可能以初始化字段如果沒有零類型D。當D::D()被明確聲明爲private時,可能發生這種情況。

在Java中,所有參考類型都有已知的zero-value,所以一個字段總是可以被初始化。

Java也做了一堆的工作,以確保*所有final領域在第一次使用前,構造函數結束之前,所以,而Java有一個像C++的const字段初始化需求的要求,它只是在構造函數重載this.fieldName = <expression>初始化,體表示字段初始化。

  • :在構造函數拋出模異常,重寫的方法從基類調用等
+0

謝謝你的答案。然而,我對「零值」這個詞有些厭倦,因爲它聽起來像是在談論整數0,而在C++ Ox(nullptr)和Java null中,我不認爲它們是等價的。 –

+0

@Jesse,我沒有發明「零價值」這個詞。這是編程語言規範文檔等中的一個常用術語。例如。 [「Effective Go」](http://golang.org/doc/effective_go.html)表示返回參數,「當命名時,它們在函數開始時被初始化爲它們類型的零值」和[語言規範](http://golang.org/doc/go_spec.html#The_zero_value)將'nil'定義爲指針類型的零值。 –

+0

感謝您提供有關「零價值」的鏈接。正如你可能知道的,不同的語言在類似的概念上使用不同的術語,比如[here](http://stackoverflow.com/questions/1350819/c-free-store-vs-heap)。所以,我會建議區分這兩者。 –

8

C++

ClassType t(initialization arguments); 

ClassType * pt; 

之間(設置爲NULL)的差值,後者不需要被初始化。前者確實如此。把它看作一個整數。你不能有一個沒有值的int,但是你可以有一個沒有值的int指針。

因此,當您有:

class ClassType 
{ 
    OtherClass value; 
    OtherClass * reference; 
}; 

然後聲明:

ClassType object; 

value自動創建的OtherClass一個實例。因此,如果OtherClass有初始化,它必須在ClassType構造函數中完成。但是,reference只是一個指針(內存中的地址),可以保持未初始化。如果你想的OtherClass實例必須使用

object.reference = new OtherClass(initialization arguments); 

的Java

只有

class ClassType 
{ 
    OtherClass reference; 
} 

這相當於在C++中的指針。在這種情況下,當您這樣做時:

ClassType object = new ClassType(); 

您不會自動創建OtherClass的實例。因此,除非需要,否則不必在構造函數中初始化任何內容。當你想要一個對象OtherClass你可以使用

object.reference = new OtherClass(); 
+0

非常容易理解的答案。 –