2016-11-02 2 views
20

Ich würde gerne wissen, ob es einen Unterschied zwischen diesem Code ist:C++ Initialisierung Felder direkt vs Initialisierung Liste in Standardkonstruktors

class Foo{ 
private: 
    int a = 0; 
public: 
    Foo(){} 
} 

Und:

class Foo{ 
private: 
    int a; 
public: 
    Foo(): a(0) {} 
} 

Und wenn ja, welche sollte bevorzugt werden? Ich weiß, dass es vorzuziehen ist, eine Initialisierungsliste als die Zuweisung im Konstruktor-Body zu verwenden, aber was ist mit Initialisierungsliste vs direkt Initialisierung in Feld Deklaration (für primitive Typen, zumindest, wie es hier der Fall ist)?

Auch, was der Fall unter:

class Foo{ 
private: 
    int a = 0; 
public: 
    Foo(){} 
    Foo(int i): a(i) {} 
} 

Wenn die Nicht-Standard-Konstruktor aufgerufen wird: „a“ zweimal initialisiert, zuerst auf 0 dann auf „i“ oder direkt auf „i“ ?

+1

Sie sind gleichwertig. Feldinitialisierung ist jedoch besser lesbar, IMO. Es ist viel einfacher zu sehen, * was * der Standardwert * ist * von der Klassendefinition. – StoryTeller

+4

... aber andererseits würde das Ändern des Standardwerts eine Neukompilierung von allem, was den Header enthält, verursachen. Kenne sie beide und wähle diejenige, die besser zu deinem Fall passt. – Quentin

+0

Laut Olaf Dietsches Antwort - Sie haben einen Fall verpasst; wobei sowohl a = 0 als auch foo: a (0) verwendet werden – UKMonkey

Antwort

16

Von cppreference - Non-static data members

Mitglied Initialisierung
1) In der Elementinitialisierung Liste des Konstrukteurs.
2) Durch einen Standard-Member-Initialisierer, der einfach eine geschweifte Klammer oder ein Gleichheitsinitialisierer ist, der in der Member-Deklaration enthalten ist und der verwendet wird, wenn das Member in der Member-Initialisierungsliste weggelassen wird.

Wenn ein Mitglied über einen Standardmemberinitialisierer verfügt und auch in der Elementinitialisierungsliste in einem Konstruktor angezeigt wird, wird der Standardmemberinitialisierer ignoriert.


Abschließend beide initializers gleichwertig sind und tun, was sie tun sollen.

Ich würde den Standard-Member-Initialisierer bevorzugen, wenn ich sowieso den Standardkonstruktor verwenden würde oder wenn alle oder die meisten Konstruktoren das Element auf denselben Wert initialisieren würden.

class Foo { 
private: 
    int a = 0; 
}; 

Wenn alle Konstrukteure jedoch das Element zu einem gewissen anderen Wert zu initialisieren, die den Standard verwendet Elementinitialisierung macht weniger Sinn, und dann eine explizite Initialisierung in den jeweiligen Konstrukteure wäre klar

class Foo { 
private: 
    int a; 
public: 
    Foo() : a(3) {} 
    Foo(int i) : a(i) {} 
}; 
9

die erste Reihe von Beispielen sind identisch miteinander.

Für das letzte Beispiel ist die C++ Standard spezifiziert, wie folgt:

12.6.2 Initialisieren Basen und den Mitgliedern

[...]

Wenn ein gegebenes nicht-statisches Datenelement hat sowohl einen Klammer-oder-Gleich-Initialisierer und einen Mem-Initialisierer, wird die vom Mem-Initialisierer angegebene Initialisierung ausgeführt, und die Klammer-oder-Gleich-Initialisierer des nicht statischen Datenelementswird ignoriert.[Beispiel:

struct A { 
int i = /∗ some integer expression with side effects ∗/ ; 
A(int arg) : i(arg) { } 
// ... 
}; 

die A (int) Konstruktor ich einfach auf den Wert von arg initialisieren, Da und die Nebenwirkungen in i der Klammer-oder-gleich-Initialisierer wird nicht stattfinden. - Ende Beispiel]

6

Die beiden sind identisch.

Eine Regel der Softwareentwicklung ist DRY - wiederholen Sie nicht. DRY besagt, dass Sie, wenn Sie es vermeiden können, dasselbe Token zweimal zu wiederholen, oder zwei identische Listen haben.

Dies ist aus ein paar Gründen. Die Beibehaltung von zwei identischen Listen ist überraschend fehleranfällig; man wird modifiziert oder hat einen Tippfehler, und der andere nicht. Es macht den Code länger, was das Lesen erschweren kann. Das Vermeiden von Copy-Paste-Coding regt dazu an, einige sehr kraftvolle und ausdrucksstarke Techniken zu verwenden, die das, was Sie tun, deutlicher machen als 17 Mal manuell.

struct foo { 
    int a; 
    foo():a(7) {} 
}; 

hier haben wir uns selbst wiederholt - die Liste der Member-Variablen, insbesondere wird zweimal aufgeführt. Einmal in der Definition von foo und wieder in der Initialisierungsliste von foo::foo. Wenn es irgendwo fehlt, erhalten Sie nicht initialisierte Daten.

struct foo { 
    int a = 7; 
    foo() {} 
}; 

Hier wiederholen wir uns nicht.

struct foo { 
    int a = 7; 
    foo() {} 
    foo(int i):a(i) {} 
}; 

Hier gibt es einige Wiederholungen, aber die Wiederholung ist unvermeidlich. Es ist jedoch minimiert.

Es gibt einige Kosten hier, weil jemand a=7 interpretieren könnte, um zu bedeuten "es beginnt immer um 7" und nicht "der Standard ist 7".

struct foo { 
    int a = 7; 
    foo():a(3) {} 
    foo(int i):a(i) {} 
}; 

Und das oben genannte ist ein schreckliches Anti-Muster.