2 Stimmen

Vererbung - das Problem der Instanziierung

Bei der Vererbung wird bei der Instanziierung einer abgeleiteten Klasse (Erstellung der abgeleiteten Klasse obj) zunächst Speicher beiseite gestellt. Danach wird der Konstruktor der abgeleiteten Klasse aufgerufen.

class Base
    {
    public:
        int m;   
        Base(int x=0)
            : m(x)
        {
        }
    };

    class Derived: public Base
    {
    public:
        double n;

            Derived(double y=0.0)   
            : n(y)
            {
        }
    };

Im obigen Beispiel wird beim Erstellen eines abgeleiteten Klassenobjekts der Konstruktor der abgeleiteten Klasse aufgerufen.

Das Problem ist, dass der Konstruktor der Basisklasse aufgerufen wird, bevor der Konstruktor der abgeleiteten Klasse aufgerufen wird, soweit ich weiß. Es gibt jedoch nirgendwo in der abgeleiteten Klasse eine ausdrückliche Anweisung, zuerst den Konstruktor der Basisklasse aufzurufen. Woher weiß der Compiler, dass der Basisklassenkonstruktor zuerst ausgeführt werden muss? ????

Ich dachte, die Antwort würde in dieser Zeile in der Deklaration der Basisklasse liegen:

class Derived: public Base

Das Problem ist jedoch, dass wir durch die Änderung der abgeleiteten Klasse wie folgt sicherstellen können, dass der Konstruktor der Basisklasse aufgerufen und initialisiert wird, bevor der Körper des abgeleiteten Konstruktors ausgeführt wird.

class Derived: public Base
    {
    public:
        double n;

            Derived(double y=0.0, int z=0)   
            : Base(z), n(y)
            {
        }
    };

Das Problem ist also, dass es im ersten Beispiel keine Anweisung gab, den Basisklassenkonstruktor zuerst aufzurufen, obwohl er tatsächlich zuerst aufgerufen wird, im zweiten geänderten Beispiel sage ich dem Compiler, dass er zuerst zum Basisklassenkonstruktor gehen soll. Weiß er nicht bereits, dass er zuerst zum Basisklassenkonstruktor gehen soll? Worin besteht der Unterschied in der Ausführung zwischen den beiden Beispielen und was sind die Schritte?

Herzliche Grüße,

3voto

RC. Punkte 26365

Der Compiler weiß, dass er den Konstruktor der Klasse Base aufrufen muss, weil Sie in der Anweisung angegeben haben, dass Sie Base erben:

class Derived : public Base

Egal was passiert, der Compiler wird die Basisklasse instanziieren (d.h. den Konstruktor aufrufen), bevor er den Konstruktor der abgeleiteten Klasse aufruft. Er muss dies tun, um die abgeleitete Klasse ordnungsgemäß zu konstruieren.

Im zweiten Beispiel, das Sie anführen: Base(z) in Ihrer Initialisierungsliste, teilen Sie dem Compiler mit, dass er die Base-Klasse explizit mit diesem Konstruktor aufrufen/initialisieren soll. Wenn Sie keinen Konstruktor in der Initialisierungsliste angeben, wird der Compiler immer den Standardkonstruktor aufrufen. Die Angabe des richtigen Konstruktors in der Initialisierungsliste (auch wenn es sich um den Standardkonstruktor handelt) gilt als beste Praxis.

1voto

Vincent Ramdhanie Punkte 100426

Wenn du einen Ball hast und daraus einen RedBall ableitest, dann brauchst du zur Erstellung eines RedBall zunächst einen Ball, den du dann rot anmalen kannst.

Um also in Ihrem Beispiel ein Objekt des Typs Abgeleitet zu erstellen, müssen Sie mit einem Objekt des Typs Base beginnen und dann das Abgeleitete daraus konstruieren. Selbst wenn Sie also den Konstruktor der Basisklasse nicht explizit aufrufen, weiß der Compiler, dass er es tun muss. Die Zeile

 class Derived: public Base

ist eigentlich die Anweisung, die den Compiler darüber informiert.

Im zweiten Fall sind Sie explizit darüber. Dies kann in dem Fall verwendet werden, wenn Sie einen Konstruktor der Basisklasse aufrufen wollen, der z.B. Poarameter hat.

0voto

nyanev Punkte 11049

Im ersten Beispiel rufen Sie den Standardkonstruktor auf. Im zweiten Beispiel rufen Sie den Konstruktor mit den Parametern

0voto

UmmaGumma Punkte 5753

Worin besteht der Unterschied in der Ausführung zwischen den beiden Beispielen und was sind die Schritte?

Der Unterschied ist, dass im ersten Fall der DEFAULT-Konstruktor aufgerufen wird, während im zweiten Fall der von Ihnen gewählte Konstruktor aufgerufen wird.

0voto

Andrei Punkte 4800

Ich denke, das Missverständnis ist, dass die C++-Sprache irgendwie schrittweise interpretiert wird, sonst kann ich mir das "wird der Compiler nicht schon den Basisklassenkonstruktor aufgerufen und initialisiert haben, bevor er den Aufruf des Konstruktors der abgeleiteten Klasse sieht" nicht erklären.

Dies ist nicht der Fall, denn der Compiler "sieht" die gesamte Kompiliereinheit, bevor er mit der Erzeugung der Ausgabe beginnt. Wenn er sieht, dass Sie einen bestimmten Konstruktor explizit aufgerufen haben - in Ihrem Fall über Base(z) - wird er diesen bei der Erstellung Ihres abgeleiteten Objekts aufrufen. Andernfalls wird es den Standardkonstruktor für Base aufrufen.

Es wäre wahrscheinlich besser, den Basiskonstruktor nicht als Base(int x = 0) zu deklarieren. Versuchen Sie, sowohl einen Konstruktor ohne Parameter als auch den Base(int)-Konstruktor zu erstellen und setzen Sie jeweils einen Haltepunkt, um genau zu sehen, welcher Konstruktor aufgerufen wird. (alternativ können Sie auch einen Haltepunkt in Base(int x = 0) setzen und den Wert von x überprüfen...)

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