Wie kann ich Basisklassenmitgliedsvariablen im abgeleiteten Klassenkonstruktor initialisieren?

123

Warum kann ich das nicht tun?

class A
{
public:
    int a, b;
};

class B : public A
{
    B() : A(), a(0), b(0)
    {
    }

};
Amrhassan
quelle
7
Fragen Sie sich, warum Sie das nicht können, was eine Frage zum Sprachdesign ist, oder fragen Sie sich, wie Sie diese Sprachbeschränkung umgehen können?
Rob Kennedy
Ich dachte, dass es eine spezielle Möglichkeit gibt, die mir nicht bekannt ist, ohne den Basiskonstruktor verwenden zu müssen.
Amrhassan
Die Basisklassenmitglieder sind bereits initialisiert, wenn Ihr Konstruktor für abgeleitete Klassen ausgeführt wird. Sie können sie zuweisen , wenn Sie Zugriff haben, oder Setter für sie aufrufen, oder Sie können dem Basisklassenkonstruktor Werte für sie bereitstellen, falls es einen geeigneten gibt. Das einzige, was Sie in der entwickelten Klasse nicht tun können, ist, sie zu initialisieren.
Marquis von Lorne

Antworten:

142

Sie können nicht initialisieren aund bin, Bweil sie keine Mitglieder von sind B. Sie sind Mitglieder von Aund Akönnen sie daher nur initialisieren. Sie können sie veröffentlichen und dann zuweisen. Dies Bwird jedoch nicht empfohlen, da dies die Kapselung zerstören würde. Erstellen Sie stattdessen einen Konstruktor in A, damit B(oder eine beliebige Unterklasse von A) diese initialisiert werden kann:

class A 
{
protected:
    A(int a, int b) : a(a), b(b) {} // Accessible to derived classes
    // Change "protected" to "public" to allow others to instantiate A.
private:
    int a, b; // Keep these variables private in A
};

class B : public A 
{
public:
    B() : A(0, 0) // Calls A's constructor, initializing a and b in A to 0.
    {
    } 
};
In silico
quelle
32
Während Ihr Beispiel korrekt ist, ist Ihre Erklärung irreführend. Es ist nicht , dass Sie nicht können initialisieren a und bin , B::B()weil sie privat sind. Sie können sie nicht initialisieren, da sie keine Mitglieder von sind class B. Wenn Sie sie öffentlich gemacht oder geschützt haben, können Sie sie im Hauptteil von zuweisenB::B() .
R Samuel Klatchko
2
Darüber hinaus macht Ihre Lösung Klasse A nicht aggregiert, was wichtig sein kann, daher muss es erwähnt werden.
Gene Bushuyev
1
@ R Samuel Klatchko: Guter Punkt. Als ich die Antwort schrieb, gab ich zunächst "Sie können nicht zugreifen aund b..." ein und änderte sie in "Sie können nicht initialisieren ...", ohne sicherzustellen, dass der Rest des Satzes Sinn machte. Beitrag bearbeitet.
In Silico
1
@Gene Bushuyev: Die Klasse im ursprünglichen Code in der Frage ist kein Aggregat (es gibt nicht statische private Mitglieder)
David Rodríguez - Dribeas
@ David - richtig, das ist ein Fehler des Benutzers, und ich versuche, zu den Absichten des Benutzers zu gelangen, indem ich oberflächlich überspringe.
Gene Bushuyev
26

Abgesehen von der Tatsache , dass sie private, da aund bsind Mitglieder A, werden sie von initialisiert werden soll A‚s Bauer, nicht von einigen Konstrukteuren der anderen Klasse (abgeleitet oder nicht).

Versuchen:

class A
{
    int a, b;

protected: // or public:
    A(int a, int b): a(a), b(b) {}
};

class B : public A
{
    B() : A(0, 0) {}
};
NPE
quelle
7

Irgendwie hat niemand den einfachsten Weg aufgelistet:

class A
{
public:
    int a, b;
};

class B : public A
{
    B()
    {
        a = 0;
        b = 0;
    }

};

Sie können nicht auf Basismitglieder in der Initialisierungsliste zugreifen, aber der Konstruktor selbst kann wie jede andere Elementmethode auf publicund protectedMitglieder der Basisklasse zugreifen .

Violette Giraffe
quelle
1
Nett. Gibt es einen Nachteil auf diese Weise?
Wander3r
2
@SaileshD: Dies kann der Fall sein, wenn Sie ein Objekt mit einem teuren Konstruktor initialisieren. Es wird zuerst standardmäßig initialisiert, wenn die Instanz von Bzugewiesen wird, und dann im BKonstruktor des Konstruktors zugewiesen . Ich denke aber auch, dass der Compiler dies noch optimieren kann.
Violette Giraffe
1
Im Inneren können class Awir uns nicht darauf verlassen aund binitialisiert werden. Bei jeder Implementierung von class C : public Awird beispielsweise möglicherweise vergessen, aufzurufen a=0;und anicht initialisiert zu bleiben.
Sparkofska
@ Parkofska, sehr wahr. Es ist am besten, die Felder standardmäßig entweder an Ort und Stelle zu initialisieren, wenn Sie sie deklarieren (class A { int a = 0;}; ) oder im Konstruktor der Basisklasse. Die Unterklassen können sie nach Bedarf in ihrem Konstruktor neu initialisieren.
Violette Giraffe
1
@ Wander3r Ein weiterer Nachteil ist, dass nicht alle Klassen Zuweisungsoperatoren haben. Einige können nur konstruiert, aber nicht zugeordnet werden. Dann bist du fertig ...
Martin Pecka
2
# include<stdio.h>
# include<iostream>
# include<conio.h>

using namespace std;

class Base{
    public:
        Base(int i, float f, double d): i(i), f(f), d(d)
        {
        }
    virtual void Show()=0;
    protected:
        int i;
        float f;
        double d;
};


class Derived: public Base{
    public:
        Derived(int i, float f, double d): Base( i, f, d)
        {
        }
        void Show()
        {
            cout<< "int i = "<<i<<endl<<"float f = "<<f<<endl <<"double d = "<<d<<endl;
        }
};

int main(){
    Base * b = new Derived(10, 1.2, 3.89);
    b->Show();
    return 0;
}

Dies ist ein funktionierendes Beispiel für den Fall, dass Sie die im abgeleiteten Klassenobjekt vorhandenen Datenelemente der Basisklasse initialisieren möchten, während Sie diese Werte über den Aufruf des Konstruktors für abgeleitete Klassen an die Schnittstelle weiterleiten möchten.

manish srivastava
quelle
1

Dies ist zwar in seltenen Fällen nützlich (wenn dies nicht der Fall wäre, hätte die Sprache dies direkt zugelassen), werfen Sie jedoch einen Blick auf die Basis aus der Mitgliedersprache . Es ist keine codefreie Lösung, Sie müssten eine zusätzliche Vererbungsebene hinzufügen, aber sie erledigt den Job. Um Boilerplate-Code zu vermeiden, können Sie die Implementierung von boost verwenden

Nikos Athanasiou
quelle
0

Warum kannst du es nicht tun? Da die Sprache es Ihnen nicht erlaubt, Mitglieder einer Basisklasse in der Initialisierungsliste der abgeleiteten Klasse zu initialisieren.

Wie können Sie das erreichen? So was:

class A
{
public:
    A(int a, int b) : a_(a), b_(b) {};
    int a_, b_;
};

class B : public A
{
public:
    B() : A(0,0) 
    {
    }
};
John Dibling
quelle
-1

Wenn Sie keine Sichtbarkeit für ein Klassenmitglied angeben, wird standardmäßig "privat" verwendet. Sie sollten Ihre Mitglieder privat oder geschützt machen, wenn Sie in einer Unterklasse auf sie zugreifen möchten.

TotoroTotoro
quelle
-1

Aggregierte Klassen wie A in Ihrem Beispiel (*) müssen ihre Mitglieder öffentlich haben und dürfen keine benutzerdefinierten Konstruktoren haben. Sie werden mit der Initialisierungsliste initialisiert, z. B. A a {0,0};oder in Ihrem Fall B() : A({0,0}){}. Die Mitglieder der Basisaggregatklasse können im Konstruktor der abgeleiteten Klasse nicht einzeln initialisiert werden.

(*) Um genau zu sein, ist das Original, wie richtig erwähnt, class Akein Aggregat aufgrund privater nicht statischer Mitglieder

Gene Bushuyev
quelle