Come posso inizializzare le variabili del membro della classe base nel costruttore della classe derivata?


123

Perché non posso farlo?

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

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

};

7
Ti stai chiedendo perché non puoi farlo, che è una domanda sul design del linguaggio, o stai chiedendo come aggirare questa limitazione del linguaggio?
Rob Kennedy,

Ho pensato che ci fosse una sorta di modo speciale per farlo di cui non sono a conoscenza, senza dover utilizzare il costruttore di base.
amrhassan

I membri della classe di base sono già inizializzati quando viene eseguito il costruttore della classe derivata. Puoi assegnarli , se hai accesso, o chiamare setter per loro, oppure puoi fornire valori per loro al costruttore della classe base, se ce n'è uno adatto. L'unica cosa che non puoi fare nella classe ideata è inizializzarli.
Marchese di Lorne

Risposte:


143

Non è possibile inizializzare ae bin Bperché non sono membri di B. Sono membri di A, quindi Apossono solo inizializzarli. Puoi renderli pubblici, quindi eseguire l'assegnazione in B, ma questa non è un'opzione consigliata poiché distruggerebbe l'incapsulamento. Invece, crea un costruttore Aper consentire B(o qualsiasi sottoclasse di A) di inizializzarli:

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
mentre il tuo esempio è corretto, la tua spiegazione è fuorviante. Non è che non puoi inizializzare a e bin B::B()perché sono privati. Non è possibile inizializzarli perché non sono membri di class B. Se li hai resi pubblici o protetti potresti assegnarli nel corpo di B::B().
R Samuel Klatchko

2
inoltre, la tua soluzione rende la classe A non aggregata, il che potrebbe essere importante, quindi deve essere menzionata.
Gene Bushuyev

1
@R Samuel Klatchko: Buon punto. Quando stavo scrivendo la risposta, inizialmente ho digitato "Non puoi accedere ae b..." e l'ho cambiato in "Non puoi inizializzare ..." senza assicurarmi che il resto della frase avesse un senso. Post modificato.
In silico

1
@Gene Bushuyev: La classe nel codice originale nella domanda non è un aggregato (ci sono membri privati ​​non statici)
David Rodríguez - dribeas

@David - corretto, che è un errore dell'utente, e sto cercando di arrivare alle intenzioni dell'utente, saltando in modo superficiale.
Gene Bushuyev

26

Lasciando da parte il fatto che sono private, poiché ae bsono membri di A, devono essere inizializzati dai Acostruttori di, non dai costruttori di qualche altra classe (derivati ​​o meno).

Provare:

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

In qualche modo, nessuno ha elencato il modo più semplice:

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

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

};

Non è possibile organi di base di accesso nella lista di inizializzazione, ma il costruttore stesso, proprio come qualsiasi altro metodo membro, può accedere publice protectedmembri della classe base.


1
Bello. C'è qualche inconveniente nel fare in questo modo?
Wander3r

2
@SaileshD: potrebbe esserci, se stai inizializzando un oggetto con un costoso costruttore. Verrà prima inizializzato per impostazione predefinita quando Bviene allocata l'istanza di , quindi verrà assegnato all'interno del Bcostruttore di. Ma penso anche che il compilatore possa ancora ottimizzarlo.
Violet Giraffe

1
All'interno class Anon possiamo fare affidamento su ae bessere inizializzati. Qualsiasi implementazione di class C : public A, ad esempio, potrebbe dimenticare di chiamare a=0;e lasciare anon inizializzato.
Sparkofska

@Sparkofska, molto vero. È meglio inizializzare i campi per impostazione predefinita quando li si dichiara ( class A { int a = 0;};) o nel costruttore della classe base. Le sottoclassi possono ancora reinizializzarle nel loro costruttore secondo necessità.
Violet Giraffe il

1
@ Wander3r Un altro svantaggio è che non tutte le classi hanno operatori di assegnazione. Alcuni possono solo essere costruiti, ma non assegnati. Allora hai finito ...
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;
}

È un esempio funzionante nel caso in cui si desideri inizializzare i membri dei dati della classe Base presenti nell'oggetto della classe Derived, mentre si desidera eseguire il push di questi valori interfacciandosi tramite la chiamata del costruttore della classe Derived.


1

Anche se questo è utile in rari casi (se così non fosse, la lingua lo avrebbe permesso direttamente), dai un'occhiata al linguaggio Base from Member . Non è una soluzione priva di codice, dovresti aggiungere un ulteriore livello di ereditarietà, ma porta a termine il lavoro. Per evitare il codice boilerplate è possibile utilizzare l'implementazione di boost


0

Perché non puoi farlo? Perché la lingua non consente di inizializzare i membri di una classe base nell'elenco di inizializzatori della classe derivata.

Come puoi farlo? Come questo:

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

Se non specifichi la visibilità per un membro della classe, il valore predefinito è "privato". Devi rendere i tuoi membri privati ​​o protetti se desideri accedervi in ​​una sottoclasse.


-1

Le classi aggregate, come A nel tuo esempio (*), devono avere i loro membri pubblici e non avere costruttori definiti dall'utente. Sono inizializzati con l'elenco degli inizializzatori, ad esempio A a {0,0};o nel tuo caso B() : A({0,0}){}. I membri della classe aggregata di base non possono essere inizializzati individualmente nel costruttore della classe derivata.

(*) Per essere precisi, come è stato giustamente ricordato, l'originale class Anon è un aggregato dovuto a membri privati ​​non statici

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.