2012-05-23 7 views
22

Betrachten Sie die folgende (vereinfachte) Situation:Können Mitgliedsvariablen verwendet werden, um andere Mitglieder in einer Initialisierungsliste zu initialisieren?

class Foo 
{ 
private: 
    int evenA; 
    int evenB; 
    int evenSum; 
public: 
    Foo(int a, int b) : evenA(a-(a%2)), evenB(b-(b%2)), evenSum(evenA+evenB) 
    { 
    } 
}; 

Wenn ich Foo wie folgt instanziieren:

Foo foo(1,3); 

dann Evena 0, EVENB 2 ist, wird aber evenSum bis 2 initialisiert werden?

Ich versuchte dies auf meiner aktuellen Plattform (iOS) und es scheint zu funktionieren, aber ich bin mir nicht sicher, ob dieser Code portabel ist.

Danke für Ihre Hilfe!

+0

Dies ist eine der gefährlichen Ecken in C++. – iammilind

+0

Codepad ist ein großartiger Ort, um solche Dinge zu überprüfen: http://codepad.org/uFgZpkwN –

+0

@Agent_L: Das wird Ihnen nicht sagen, ob der Code tragbar ist. –

Antwort

27

Diese ist gut definiert und tragbar, aber es ist potenziell fehleranfällig.

Mitglieder werden in der Reihenfolge initialisiert sie in der Klasse Körper deklariert ist, nicht die Reihenfolge, wie sie in der Initialisierungsliste aufgeführt sind. Wenn Sie also den Klassenkörper ändern, kann dieser Code automatisch fehlschlagen (obwohl viele Compiler dies erkennen und eine Warnung ausgeben).


1. Aus [class.base.init] in der C++ Standard (s):

In einem nicht zum Delegieren Konstruktor Initialisierung erfolgt in der folgenden Reihenfolge:

  • Erstens, und nur für den Konstruktor der meisten abgeleiteten Klasse (1,8) werden in die Reihenfolge, wie sie von links nach rechts Traversierung des gerichteten azyklischen graph von Basisklassen auf einer Tiefe-zuerst erscheinen virtuelle Basisklassen initialisiert, wo von links nach rechts“ist die Reihenfolge des Erscheinens der Basisklassen in der abgeleiteten Klasse Basis-Bezeichner-Liste.
  • Dann werden direkte Basisklassen in der Deklarationsreihenfolge initialisiert, wie sie in der Basisspezifiziererliste erscheinen (unabhängig von der Reihenfolge der mem-Initialisierer).
  • Dann werden nicht statische Datenelemente in der Reihenfolge initialisiert, in der sie in der Klassendefinition deklariert wurden (wiederum unabhängig von der Reihenfolge der mem-Initialisierer).
  • Schließlich wird die Verbindung-Anweisung des Konstruktorrumpf ausgeführt.

(Hervorhebungen sind mein.)

Dieser Abschnitt des Standard dann auf einem Beispiel der Verwendung Membervariablen zu geben gehen andere Elementvariablen zu initialisieren.

+0

ok also, wenn Foo eine Basisklasse Bar (nicht-virtuelle öffentliche Vererbung) und ich Bars Konstruktor in der Initialisierungsliste hat, wird es immer vor allen Mitglied Initialisierungen ausgeführt werden, auch wenn ich es am Ende der Initialisierungsliste setzen? – Pontomedon

+0

@ Pontomedon: Ja. Basisklassenkonstruktoren werden immer zuerst aufgerufen (auch wenn sie überhaupt nicht in der Liste enthalten sind). –

6

Mitglieder werden in der Reihenfolge initialisiert, in der sie in der Klassendefinition deklariert sind. Solange Ihre Initialisierungsliste dieser Reihenfolge entspricht, sollte es in Ordnung sein.

0

Dies kompilierte auch ohne Fehler auf g ++ 4.0.3 (6 Jahre alt jetzt).

Ich bin sicher, dass dies auf jeden einigermaßen aktuellen Compiler gut kompilieren wird.

8

Ja, vorausgesetzt, sie wurden bereits konstruiert. Vergessen Sie einfach nicht , dass die Reihenfolge der Konstruktion ist die Reihenfolge der Deklarationen in der Klassendefinition, nicht die Reihenfolge der Initialisierer in der Konstruktor. Und dass der Compiler Ihnen normalerweise nicht sagen wird, ob Sie eine Variable verwenden, bevor es erstellt wurde. In Ihrem Fall zum Beispiel wenn Sie evenSum an die Spitze der Klasse bewegen, haben Sie Verhalten nicht definiert (weil seine initializer nicht initialisierten Mitglieder verwendet), auch obwohl in Ihrem Konstruktor Sie initialisieren evenA und evenB lexikalisch vor evenSum.