2013-02-19 10 views
6

Ich überschreibe einige Code, um globale Variablen zu beseitigen, und machte eine Klassenkonstruktor/Destruktor-Bereinigung von einigen Drittanbieter-Bibliothek Ressourcen, aber ich bin besorgt über etwas Code, der ein Mitglied aus einem anderen Mitglied in der Klasseninitialisierungsliste initialisiert.Kann ich C++ - Klassenmitglieder verwenden, die in der Initialisierungsliste initialisiert wurden, später in der Liste?

class MyPodofoDocument { 
public: 
    // generates pdf to stream 
    MyPodofoDocument(std::stringstream *pStringStream) 
     : device(pStringStream), document(&device) 
    { 
    } 
private: 
    PoDoFo::PdfOutputDevice device; 
    PoDoFo::PdfStreamedDocument document; 
    PoDoFo::PdfPainter painter; 
}; 

Der Code, der diese Klasse verwendet, muss nicht alle Details, um zu sehen, die die Bibliothek gehen in Verwendung, aber die Art, wie ich sie verstecken macht es abhängig von Mitgliedern mit anderen Mitgliedern zu initialisieren, bevor es die Hits Konstruktors tatsächlicher Codeblock, wo er einen gültigen Zeiger hat.

Es funktioniert in einem Unit-Test-Skelett, so ist meine Frage im Grunde: "Ist das in Ordnung, tragbar und sicher?"

Antwort

8

Die Mitglieder werden in der Reihenfolge ihrer Deklaration initialisiert, von oben nach unten

PoDoFo::PdfOutputDevice device; 
PoDoFo::PdfStreamedDocument document; 
PoDoFo::PdfPainter painter; 

so ist es sicher device zu verwenden document zu initialisieren.

+0

Darüber hinaus ist es legal, die Adresse zu erhalten oder einen Verweis auf ein noch zu erstellendes Element zu binden (dh Sie können später einen Verweis auf ein deklariertes Element übergeben, wenn der Empfänger * das Objekt * nicht verwendet, sondern nur den Verweis/Zeiger). –

+0

Es ist zulässig, zu übergeben, aber semantisch falsch, da Sie einen Zeiger auf etwas geben würden, das noch nicht konstruiert wurde. –

+0

@AlexChamberlain: Nichts ist daran falsch, aber wahrscheinlich braucht es einen Doppel-Check. – GManNickG

4

Art von. Die Regeln lauten, dass die Mitgliedsvariablen in der Reihenfolge initialisiert werden, in der sie in der Klassendeklaration deklariert sind.

In Ihrem Fall ist es in Ordnung, da device vor document deklariert ist.

Im folgenden Fall haben wir trotz der Reihenfolge der Initialisierungsliste ein undefiniertes Verhalten.

class A { 
public: 
    A(int i) : b(i), a(b) { } 
private: 
    int a; 
    int b; 
} 
+1

Obwohl es sicher ist * solange die Abhängigkeiten zwischen den Variablen die gleiche wie die Deklarationsreihenfolge sind *, IMHO ist es nicht sehr gute Praxis. Ein solcher Code sollte wann immer möglich vermieden werden, schon allein deshalb, weil es zu einfach ist, die Deklarationsreihenfolge während des Refactorings Ihrer Klasse versehentlich zu ändern, wodurch UB ausgelöst wird, wie das Beispiel von Alex zeigt. Mit anderen Worten, das ist korrekt, aber sehr fragil. – syam

+1

@syam: Ich bin mir nicht sicher ... Ich dachte immer dasselbe, aber es ist sehr einfach, den Compiler zu warnen, wenn die Initialisierer nicht in Ordnung sind (der Compiler wird dich auf die potentielle UB aufmerksam machen) und in einigen Fällen kann es hilfreich sein, auf ein anderes Mitglied desselben Typs zu verweisen. –

+0

Es ist nicht nur nützlich für Dinge der gleichen Art von Kurs; alles, wo es eine echte Abhängigkeit zwischen Variablen gibt, aber wo Sie auch die Variablen speichern müssen. –