2009-06-18 7 views
10

Ich bin mir sicher, das ist eine wirklich einfache Frage. Der folgende Code zeigt, was ich versuche zu tun:Richtiger Weg, um eine C++ Membervariable bedingt zu initialisieren?

class MemberClass { 
public: 
    MemberClass(int abc){ } 
}; 

class MyClass { 
public: 
    MemberClass m_class; 
    MyClass(int xyz) { 
     if(xyz == 42) 
      m_class = MemberClass(12); 
     else 
      m_class = MemberClass(32); 
    } 
}; 

Dies gilt nicht kompilieren, weil m_class mit einem leeren Konstruktor erstellt werden (die es nicht gibt). Was ist der richtige Weg? Meine Vermutung ist Zeiger und Instanziieren m_class mit new, aber ich hoffe, es gibt einen einfacheren Weg.

Edit: Ich hätte früher gesagt werden sollen, aber mein aktuelles Problem hat eine zusätzliche Komplikation: Ich muss eine Methode vor der Initialisierung m_class aufrufen, um die Umgebung einzurichten. Also:

class MyClass { 
public: 
    MemberClass m_class; 
    MyClass(int xyz) { 
     do_something(); // this must happen before m_class is created 
     if(xyz == 42) 
      m_class = MemberClass(12); 
     else 
      m_class = MemberClass(32); 
    } 
}; 

Ist es möglich, dies mit ausgefallenen Initialisierungslistentricks zu erreichen?

Antwort

24

den bedingten Operator verwenden. Wenn der Ausdruck größer ist, verwenden Sie eine Funktion

class MyClass { 
public: 
    MemberClass m_class; 
    MyClass(int xyz) : m_class(xyz == 42 ? 12 : 32) { 

    } 
}; 

class MyClass { 
    static int classInit(int n) { ... } 
public: 
    MemberClass m_class; 
    MyClass(int xyz) : m_class(classInit(xyz)) { 

    } 
}; 

Um eine Funktion vor der Initialisierung m_class aufrufen, können Sie eine Struktur vor diesem Mitglied und nutzen RAII

class MyClass { 
    static int classInit(int n) { ... } 
    struct EnvironmentInitializer { 
     EnvironmentInitializer() { 
      do_something(); 
     } 
    } env_initializer; 
public: 
    MemberClass m_class; 
    MyClass(int xyz) : m_class(classInit(xyz)) { 

    } 
}; 

Dieser Aufruf wird do_something() vor m_class initialisiert. Beachten Sie, dass Sie nicht statische Elementfunktionen von MyClass aufrufen dürfen, bevor die Konstruktorinitialisierungsliste abgeschlossen ist. Die Funktion müsste Mitglied ihrer Basisklasse sein und die Basisklasse "ctor" muss bereits abgeschlossen sein, damit sie funktioniert.

Beachten Sie auch, dass die Funktion natürlich immer für jedes separate Objekt aufgerufen wird - nicht nur für das erste erstellte Objekt. Wenn Sie das tun wollen, können Sie eine statische Variable innerhalb der Konstruktor der initializer erstellen:

class MyClass { 
    static int classInit(int n) { ... } 
    struct EnvironmentInitializer { 
     EnvironmentInitializer() { 
      static int only_once = (do_something(), 0); 
     } 
    } env_initializer; 
public: 
    MemberClass m_class; 
    MyClass(int xyz) : m_class(classInit(xyz)) { 

    } 
}; 

Es ist das Komma-Operator. Beachten Sie, dass jede Ausnahme von do_something unter Verwendung eines geworfen fangen funktions versuchen blockieren

class MyClass { 
    static int classInit(int n) { ... } 
    struct EnvironmentInitializer { 
     EnvironmentInitializer() { 
      static int only_once = (do_something(), 0); 
     } 
    } env_initializer; 
public: 
    MemberClass m_class; 
    MyClass(int xyz) try : m_class(classInit(xyz)) { 

    } catch(...) { /* handle exception */ } 
}; 

Die do_something Funktion wird beim nächsten Mal wieder aufgerufen werden, wenn es diese Ausnahme ausgelöst hat, dass die MyClass Objekt verursacht nicht erstellt werden.Hoffe, das hilft :)

+0

Danke! Ich habe meine Frage aktualisiert - ich muss vor dem Erstellen von m_class eine Methode ausführen. Ist das möglich? Vielleicht könnte ich es in "classInit" machen, aber das wäre nicht sehr elegant. –

+0

Viel gelernt, danke! :-) –

+0

Als ich den Comma-Operator sah, wusste ich, dass die Person, die diese Frage beantwortete, clever war. Als ich die Verwendung der Funktion try block sah, wusste ich, dass die Person, die diese Frage beantwortete, litb war. Als ich die Person sah, war diese Frage zu beantworten litb ... na ja ich denke, ich es schon kannte ihn war :) – mmocny

5

die Initialisiererliste Syntax:

class MyClass { 
public: 
    MemberClass m_class; 
    MyClass(int xyz) : m_class(xyz == 42 ? MemberClass(12) : MemberClass(32) 
           /* see the comments, cleaner as xyz == 42 ? 12 : 32*/) 
    { } 
}; 

Wahrscheinlich Reiniger mit einer Fabrik:

MemberClass create_member(int x){ 
    if(xyz == 42) 
    return MemberClass(12); 
    // ... 
} 

//... 
MyClass(int xyz) : m_class(create_member(xyz)) 
+2

Sie erstellen eine unbenannte Objekt, und dann die Kopie-Ctor mit dem Element zu initialisieren, anstatt es direkt zu initialisieren. –

+0

Ja, ich tippe zu schnell für mein eigenes Wohl. Persönlich würde ich die Logik immer noch in MemberClass oder eine Fabrik verschieben. –

5
MyClass(int xyz) : m_class(xyz==42 ? 12 : 32) {} 

Ihre überarbeitete Frage zu beantworten, dass ein bisschen schwierig bekommen. Der einfachste Weg wäre m_class einen Zeiger zu machen. Wenn Sie es wirklich als Datenelement möchten, müssen Sie kreativ werden. Erstellen Sie eine neue Klasse (am besten, wenn sie intern in MyClass definiert ist). Es muss die Funktion sein, die aufgerufen werden muss. Fügen Sie es zuerst unter den Deklarationen von Datenmitgliedern hinzu (dies wird es zum ersten Mal machen).

class MyClass 
{ 
    class initer { public: initer() { 
        // this must happen before m_class is created 
        do_something();       
        } 
        } 

    initer  dummy; 
public: 

    MemberClass m_class; 
    MyClass(int xyz) : m_class(xyz==42? 12 : 43) 
    { 
     // dummy silently default ctor'ed before m_class. 
    } 
}; 
0

Oder:

class MemberClass { 
public: 
    MemberClass(int abc){ } 
}; 

class MyClass { 
public: 
    MemberClass* m_class; 
    MyClass(int xyz) { 
     if(xyz == 42) 
      m_class = new MemberClass(12); 
     else 
      m_class = new MemberClass(32); 
    } 
}; 

Wenn Sie irgendwie immer noch die gleiche Syntax behalten möchten. Die Initalisierung von Mitgliedern ist jedoch effizienter.

0

Versuchen Sie folgendes:

class MemberClass 
{ 
public:  
    MemberClass(int abc = 0){ } 
}; 

Dieses es einen Standardwert gibt, und Ihr Standard-Konstruktor.

+1

Manchmal gibt es Gründe, keinen Standardkonstruktor zu haben. –

+0

Sie müssten einen Standardparameter haben, der es in einen bekannten Zombie-Zustand versetzt. Nicht ideal, aber manchmal der einzige Weg. –

+0

Manchmal gibt es Gründe, die Standard-Konstruktor privat zu machen, aber es sollte immer da sein ... – Kieveli

0

die Initialisierung haben, passiert nach anderen Sachen passieren, Sie brauchen in der Tat Zeiger verwenden, etwa so:

class MyClass { 
public: 
    MemberClass * m_pClass; 
    MyClass(int xyz) { 
     do_something(); // this must happen before m_class is created 
     if(xyz == 42) 
      m_pClass = new MemberClass(12); 
     else 
      m_pClass = new MemberClass(32); 
    } 
}; 

Der einzige Unterschied ist, dass Sie Elementvariablen als m_pClass->counter zugreifen müssen werden statt m_class.counter und delete m_pClass im Destruktor.

+0

Die Alternative ist Myclass in einige Dummy-Zombie-Zustand und wieder init später zu initialisieren, wenn Sie den Wert kennen. Aber ich würde Zeiger bevorzugen. –

Verwandte Themen