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)
    {
    }

};

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.
    {
    } 
};

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) {}
};

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.


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.


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


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) 
    {
    }
};

-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.


-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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.