11 Stimmen

Konstruktor und Initialisierung von benutzerdefinierten Klassen/Objekten

Ich könnte mir vorstellen, dass diese Frage bereits gestellt wurde, aber ich konnte keine passende Lösung finden, also entschuldigen Sie bitte, wenn diese Frage überflüssig ist.

Ich habe eine benutzerdefinierte Klasse

class myClass_A
{
public:
    myClass_A();          // Constructor
    myFunction_A();       // Some function from Class A
};

Jetzt habe ich eine andere benutzerdefinierte Klasse, die ein Mitglied vom Typ der myClass_A

class myClass_B
{
public:
    myFunction_B();       // Some function from Class B

private:
    myClass_A m_instance; // Instance of Class A
}

Jetzt myFunction_B() will die Methode myFunction_A() de m_instance in etwa so:

myClass_B::myFunction_B()
{
    m_instance.myFunction_A();
}

Wenn ich jetzt meinen Code kompiliere (der im Grunde wie das oben gepostete Beispiel ist), wird er ohne Warnungen oder Fehler erfolgreich sein. Also meine Fragen wären:

A. Wird der Konstruktor in diesem Beispiel aufgerufen?

B. Kann ich tatsächlich Methoden von einem nicht initialisierten Objekt aufrufen?

C. Angenommen, der Konstruktor wird nicht aufgerufen, aber ich kann trotzdem die Methoden von diesem Objekt aus aufrufen -> Das bedeutet immer noch, dass die Mitglieder meiner Klasse nicht initialisiert sind?

Tut mir leid, wenn diese Fragen ein bisschen dumm sind, aber ich habe das Gefühl, dass ich im Moment nicht so recht weiß, was ich tun soll.

9voto

LucasB Punkte 2943

Dies sind sehr gute und wichtige Fragen.

In Bezug auf A:

Vor der Ausführung des Konstruktorkörpers erzeugt C++ Code, der automatisch die Standardkonstruktor aller aggregierten (d. h. Mitglieds-) Objekte Ihrer Klasse. Im Wesentlichen wird der folgende Code umgewandelt:

class myClass_B {
public:
    myClass_B()
    {
        m_instance.foo();
        m_pInstance->foo();
    }
private:
    myClass_A m_instance;
    myClass_A* m_pInstance;
};

in den folgenden Code:

class myClass_B {
public:
    myClass_B()
        : m_instance()
        , m_pInstance()
    {
        m_instance.foo();
        m_pInstance->foo();
    }
private:
    myClass_A m_instance;
    myClass_A* m_pInstance;
};

Die beiden Zeilen, die der Compiler automatisch eingefügt hat, heißen Initialisierungsliste ruft es den Standardkonstruktor jedes Aggregatobjekts auf vor wird der Körper Ihres Konstruktors ausgeführt. Bitte beachten Sie, dass die zweite, m_pInstance() ruft den "Standardkonstruktor von Zeiger ", das eine nicht initialisierter Zeiger Dies ist fast immer nicht das, was Sie wollen. Siehe unten, wie man das beheben kann.

Nehmen wir nun an, dass der Konstruktor von myClass_A hat die Signatur myClass_A(int someNumber) d.h. es benötigt ein Argument. Dann, C++ kann nicht automatisch die Initialisierungsliste für myClass_B da es nicht weiß, welche Nummer es übergeben soll myClass_A Konstrukteur. Es wird einen Compilerfehler auslösen, der sich wahrscheinlich über einen fehlenden Standardkonstruktor für myClass_A . Sie müssen zum Beispiel die Initialisierungsliste selbst schreiben:

class myClass_B {
public:
    myClass_B()
        : m_instance(21)
        , m_pInstance(new myClass_A(21))
    {
        m_instance.foo();
        m_pInstance->foo();
    }
private:
    myClass_A m_instance;
    myClass_A* m_pInstance;
};

Dies ist korrekter Code, der Folgendes aufruft myClass_A Konstruktor mit dem Wert 21 für den Parameter someNumber . Dies zeigt auch, wie man einen Zeiger korrekt initialisiert: Er muss auf ein neu zugewiesenes Objekt zeigen.

In Bezug auf B:

Im Gegensatz zu einigen anderen, die sagen, du kannst! (Probieren Sie es aus)

Dies führt jedoch zu einem unerwarteten Verhalten, was nicht erwünscht ist. (Dazu gehört auch, dass es nur dann funktioniert, wenn die Planeten richtig ausgerichtet sind.) Es wird höchstwahrscheinlich abstürzen, aber es ist nicht garantiert, dass es abstürzt. Das kann zu langen Debugging-Nächten führen. Wenn Ihr Compiler schlau ist, könnte er dies erkennen und warnen aber es wird Ihnen keinen Fehler anzeigen.

Beachten Sie auch, dass für Nicht-Zeiger-Aggregatobjekte, die einen Standardkonstruktor haben, der Standardkonstruktor wird angerufen werden, und alles wird gut sein. Das Problem entsteht, wenn Sie eingebaute Typen oder Zeiger verwenden. Dies ist die Verwendung von nicht initialisierten Variablen und eine der häufigsten Ursachen für einen Fehler. Wenn Ihr Code etwas völlig Seltsames tut, überprüfen Sie immer, ob Sie alle Ihre Variablen initialisiert haben. Es sollte ein Reflex werden, für jede Member-Variable einen Eintrag in die Initialisierungsliste zu machen, auch wenn sie den Standardkonstruktor aufruft. Das macht die Dinge klar.

In Bezug auf C:

Ja. Siehe B für Einzelheiten. Das Interessante daran ist, dass Ihre Methode garantiert funktioniert, wenn die von Ihnen aufgerufene Methode nicht den "this"-Zeiger verwendet (d. h. keine Attributvariable verwendet und keine Methode aufruft, die eine Attributvariable verwendet). Was passiert, wenn Sie eine Methode auf einem nicht initialisierten Objekt aufrufen, ist, dass das "this"-Objekt innerhalb der Methode (d.h. auch alle Attributvariablen) zufälliger Speicher ist. Der Code der Methode wird zwar ausgeführt, verwendet aber zufälligen Speicher, und das ist es, was scheitert.

Ich hoffe, das klärt die Sache ein wenig auf.

4voto

Alok Save Punkte 196241

Wird der Konstruktor in diesem Beispiel aufgerufen?

Ja
Die Konstrukteure für myClass_B wird nach dem Aufruf des Konstruktors aufgerufen myClass_A() ist abgeschlossen.
Normalerweise initialisieren Sie die myClass_B Objekt im Konstruktor von my_class_A unter Verwendung einer
Mitgliederinitialisierungsliste .

Kann ich tatsächlich Methoden von einem nicht initialisierten Objekt aufrufen?

Wenn ein Objekt erstellt wird, wird immer sein Konstruktor aufgerufen. Solange Sie ein Objekt nicht konstruieren, können Sie keine Methode dafür aufrufen. Dies ist der eigentliche Zweck der Konstruktoren, ein Objekt zu erstellen und zu initialisieren.
Wenn Sie also ein Objekt haben, ist es niemals uninitialisiert.

Wenn Sie sich auf Zeiger beziehen, dann sind Zeiger an sich keine Objekte, sondern können auf ein gültiges oder ungültiges Objekt zeigen. Das Derefenzieren eines Zeigers (für den Aufruf einer Mitgliedsfunktion oder was auch immer), der nicht auf ein gültiges Objekt zeigt, führt zu Unbestimmtes Verhalten .

Angenommen, der Konstruktor wird nicht aufgerufen, aber ich kann trotzdem die Methoden von diesem Objekt aus aufrufen -> Das bedeutet immer noch, dass die Mitglieder meiner Klasse nicht initialisiert sind?

Die Antwort auf die zweite Frage beantwortet dies.

2voto

Jon Punkte 411383

Klassenmitglieder werden vom Compiler automatisch mit dem Standardkonstruktor initialisiert, es sei denn, Sie geben im Konstruktor Ihrer eigenen Klasse etwas anderes an Initialisierungsliste . Also ja, der Konstruktor wird aufgerufen.

Sie können dies selbst testen, indem Sie entweder:

  • dass der Konstruktor eine Meldung ausgibt (die Sie sehen werden), oder
  • den Konstruktor privat zu machen oder einen Konstruktor mit Argumenten zu deklarieren, so dass der Standardkonstruktor nicht mehr automatisch erzeugt wird (der Compiler verweigert die Kompilierung, da er das Aggregatobjekt nicht mehr standardmäßig konstruieren kann)

Wenn Sie Methoden aufrufen oder auf Mitglieder eines Objekts zugreifen, das nicht initialisiert wurde oder das bereits zerstört wurde (beide Szenarien sind möglich), müssen Sie _undefiniertes Verhalten_ .

2voto

zvrba Punkte 23708

A. ja: Konstruktoren von Mitgliedern werden vor dem Konstruktor der enthaltenden Klasse aufgerufen. B. ja, aber das Ergebnis ist ein undefiniertes Verhalten (= alles kann passieren; sogar scheinbar funktionieren.).

0voto

Jeeva Punkte 4501

A. Der Konstruktor für Ihre Klasse myClass_A wird aufgerufen, sobald das Objekt der Klasse myClass_B instanziiert wird.

B. Sie können keine Methoden von unintialisierten Objekten aufrufen, zum Beispiel

myClass_A *pObj;
pObj->myFunction_A();

löst eine Ausnahme aus.

C. Sie können eine Methode niemals erfolgreich aufrufen, ohne dass das Objekt vollständig konstruiert ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X