Jak mogę zainicjować zmienne składowe klasy bazowej w konstruktorze klasy pochodnej?

123

Dlaczego nie mogę tego zrobić?

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

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

};
amrhassan
źródło
7
Czy pytasz, dlaczego nie możesz tego zrobić, co jest pytaniem dotyczącym projektowania języka, czy też pytasz, jak obejść to ograniczenie językowe?
Rob Kennedy,
Pomyślałem, że jest na to jakiś specjalny sposób, którego nie jestem świadomy, bez konieczności używania konstruktora podstawowego.
amrhassan
Elementy członkowskie klasy bazowej są już zainicjowane do czasu uruchomienia konstruktora klasy pochodnej. Możesz je przypisać , jeśli masz dostęp, lub wywołać dla nich metody ustawiające, lub możesz podać ich wartości do konstruktora klasy bazowej, jeśli istnieje odpowiedni. Jedyną rzeczą, której nie możesz zrobić w stworzonej klasie, jest zainicjowanie ich.
Markiz Lorne

Odpowiedzi:

143

Nie można zainicjować ai bna Bponieważ nie są członkami B. Są członkami A, więc tylko Amogą je inicjalizować. Możesz je upublicznić, a następnie wykonać przypisanie B, ale nie jest to zalecana opcja, ponieważ zniszczyłaby hermetyzację. Zamiast tego utwórz konstruktor w programie, Aaby umożliwić B(lub dowolną podklasę A) ich zainicjowanie:

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
źródło
32
chociaż twój przykład jest poprawny, twoje wyjaśnienie jest mylące. To nie to, że nie może się zainicjować a i bna B::B()ponieważ są prywatne. Nie możesz ich zainicjować, ponieważ nie są członkami class B. Jeśli upubliczniłeś je lub chroniłeś, możesz przypisać je w treści B::B().
R Samuel Klatchko,
2
dodatkowo Twoje rozwiązanie sprawia, że ​​klasa A nie jest agregowana, co może być ważne, więc należy o tym wspomnieć.
Gene Bushuyev,
1
@R Samuel Klatchko: Słuszna uwaga. Kiedy pisałem odpowiedź, początkowo wpisałem „Nie możesz uzyskać dostępu ai b...” i zmieniłem to na „Nie możesz zainicjować ...” bez upewnienia się, że reszta zdania ma sens. Edytowano post.
In silico
1
@Gene Bushuyev: Klasa w oryginalnym kodzie w pytaniu nie jest agregatem (są niestatyczni członkowie prywatni)
David Rodríguez - dribeas
@David - poprawne, co jest błędem użytkownika i staram się dotrzeć do intencji użytkownika, pomijając powierzchowne.
Gene Bushuyev
26

Pomijając fakt, że są private, ponieważ są członkami ai bsą członkami A, mają być inicjowane przez Akonstruktory, a nie przez konstruktory innej klasy (pochodne lub nie).

Próbować:

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
źródło
7

Jakoś nikt nie wymienił najprostszego sposobu:

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

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

};

Nie możesz uzyskać dostępu do podstawowych elementów członkowskich na liście inicjalizatora, ale sam konstruktor, podobnie jak każda inna metoda członkowska, może uzyskać dostęp publici protectedelementy członkowskie klasy bazowej.

Fioletowa żyrafa
źródło
1
Miły. Czy jest to jakaś wada?
Wander3r
2
@SaileshD: może być, jeśli inicjalizujesz obiekt za pomocą kosztownego konstruktora. Po Bprzydzieleniu instancji programu zostanie ona najpierw zainicjowana domyślnie , a następnie zostanie przypisana wewnątrz Bkonstruktora. Ale myślę też, że kompilator nadal może to zoptymalizować.
Violet Giraffe
1
Wewnątrz class Anie możemy polegać ai bjest inicjowany. Każda implementacja class C : public A, na przykład, może zapomnieć o wywołaniu a=0;i pozostawić aniezainicjowaną.
Sparkofska
@Sparkofska, bardzo prawda. Najlepiej jest domyślnie inicjalizować pola w miejscu podczas ich deklarowania ( class A { int a = 0;};) lub w konstruktorze klasy bazowej. Podklasy nadal mogą w razie potrzeby ponownie zainicjować je w swoim konstruktorze.
Violet Giraffe
1
@ Wander3r Kolejną wadą jest to, że nie wszystkie klasy mają operatory przypisania. Niektóre można tylko zbudować, ale nie można do nich przypisać. Wtedy gotowe ...
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;
}

Jest to działający przykład na wypadek, gdybyś chciał zainicjować składowe danych klasy bazowej obecne w obiekcie klasy pochodnej, podczas gdy chcesz przekazać te wartości za pośrednictwem wywołania konstruktora klasy pochodnej.

manish srivastava
źródło
1

Chociaż jest to przydatne w rzadkich przypadkach (gdyby tak nie było, język pozwoliłby na to bezpośrednio), spójrz na idiom Base from Member . To nie jest rozwiązanie wolne od kodu, musiałbyś dodać dodatkową warstwę dziedziczenia, ale wykonuje to zadanie. Aby uniknąć standardowego kodu, możesz użyć implementacji boost

Nikos Athanasiou
źródło
0

Dlaczego nie możesz tego zrobić? Ponieważ język nie pozwala na inicjalizację elementów członkowskich klasy bazowej na liście inicjatorów klasy pochodnej.

Jak możesz to zrobić? Lubię to:

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
źródło
-1

Jeśli nie określisz widoczności dla członka klasy, domyślnie będzie to „prywatna”. Powinieneś uczynić swoich członków prywatnymi lub chronionymi, jeśli chcesz uzyskać do nich dostęp w podklasie.

TotoroTotoro
źródło
-1

Klasy agregujące, takie jak A w Twoim przykładzie (*), muszą mieć swoje elementy członkowskie jako publiczne i nie mogą mieć konstruktorów zdefiniowanych przez użytkownika. Są one inicjowane z listą inicjalizacyjną, np. A a {0,0};Lub w twoim przypadku B() : A({0,0}){}. Elementy członkowskie podstawowej klasy zagregowanej nie mogą być indywidualnie inicjowane w konstruktorze klasy pochodnej.

(*) Mówiąc dokładniej, jak słusznie wspomniano, oryginał class Anie jest agregatem ze względu na prywatne niestatyczne elementy członkowskie

Gene Bushuyev
źródło