2009-04-07 8 views
9

Wird die Initialisierungsliste immer vor dem Konstruktorcode bearbeitet?Wird die Initialisierungsliste immer vor dem Konstruktorcode verarbeitet?

Mit anderen Worten, wird der folgende Code immer <unknown> drucken, und die konstruierte Klasse hat „bekannt“ als Wert für source_ (wenn die globale Variable something ist true)?

class Foo { 
    std::string source_; 
public: 
    Foo() : source_("<unknown>") { 
    std::cout << source_ << std::endl; 
    if(something){ 
     source_ = "known"; 
    } 
    } 
}; 

Antwort

12

Ja, wird es, wie C++11: 12.6.2.

Der Hauptgrund für die Verwendung von init-lists ist es, dem Compiler bei der Optimierung zu helfen. Init-Listen für nicht-grundlegende Typen (d. H. Klassenobjekte anstelle von int, float usw.) können im Allgemeinen vor Ort konstruiert werden.

Wenn Sie das Objekt dann im Konstruktor zuweisen, führt dies in der Regel zur Erzeugung und Zerstörung temporärer Objekte, was ineffizient ist.

Init-Listen können dies vermeiden (wenn der Compiler es natürlich tut, aber die meisten von ihnen sollten).

Das folgende vollständige Programm wird 7 ausgeben aber das ist für einen bestimmten Compiler (CygWin g ++), so dass es dieses Verhalten nicht mehr als das Beispiel in der ursprünglichen Frage garantiert.

Wie jedoch die zitieren in den ersten Absatz oben, der Standard tut tatsächlich garantieren.

#include <iostream> 
class Foo { 
    int x; 
    public: 
     Foo(): x(7) { 
      std::cout << x << std::endl; 
     } 
}; 
int main (void) { 
    Foo foo; 
    return 0; 
} 
+0

Wundert mich, warum Leute nicht erst versuchen, die Quelle zu kompilieren :) – arul

+1

@arul: @dehmann darf nur den einen Compiler haben. Wenn dieses Verhalten von der Implementierung definiert ist, hilft das Kompilieren nicht. Sie müssten sich auf den Standard beziehen. – paxdiablo

+1

Ja, nur kompilieren und ausprobieren sagt dir nicht, ob das überall gleich funktioniert. – Frank

7

Ja, C++ erstellt alle Mitglieder vor dem Aufruf des Constructur-Codes.

7

Wie bereits beantwortet, werden die Initialisierungslisten vollständig ausgeführt vor Eingabe des Konstruktorblocks. Daher ist es absolut sicher, (initialisierte) Member im Konstruktor-Body zu verwenden.

Sie haben einen Kommentar in der akzeptierten Antwort gemacht zu beziehen sie auf die Konstruktorargumente zu hat, aber nicht das Mitglied Vars innerhalb des Konstruktors Blockes. Du nicht.

Es ist möglich, dass Sie die Tatsache, dass Sie verwechselte sollten beziehen sich auf Parameter und nicht an das Mitglied Attribute innerhalb die Initialisierung Liste. Als ein Beispiel kann eine Klasse X gegeben, dass zwei Mitglieder hat (a_ und B_) vom Typ int, der folgende Konstruktor schlecht definiert werden kann:

hier
X::X(int a) : a_(a), b(a_*2) {} 

das mögliche Problem, dass der Aufbau der Elemente in dem Die Initialisierungsliste hängt von der Reihenfolge der Deklaration in der Klasse und nicht von der Reihenfolge ab, in der Sie die Initialisierungsliste eingeben. Wenn die Klasse definiert wurden:

class X 
{ 
public: 
    X(int a); 
private: 
    int b_; 
    int a_; 
}; 

Dann, unabhängig davon, wie Sie die Initialisierung Liste eingeben, ist die Tatsache, dass b_ (a_ * 2) wird ausgeführt werden, bevor a_ seit der Erklärung des initialisiert Mitglieder sind zuerst b_ und später a_. Das wird einen Fehler verursachen, da Ihr Code glaubt (und wahrscheinlich davon abhängt), dass b_ doppelt so groß wie der Wert von a_ ist, und tatsächlich enthält b_ Müll.Die einfachste Lösung ist, beziehe nicht auf die Mitglieder:

X::X(int a) : a_(a), b(a*2) {} // correct regardless of how X is declared 

diese Gefahr Vermeidung der Grund ist, warum Sie vorgeschlagen werden, nicht Mitglied zu verwenden Attribute als Teil der Initialisierung von anderen Mitgliedern.