¿Cómo puedo inicializar las variables de miembros de la clase base en el constructor de clases derivadas?

123

¿Por qué no puedo hacer esto?

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

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

};
amrhassan
fuente
7
¿Se pregunta por qué no puede hacer eso, que es una pregunta de diseño de lenguaje, o se pregunta cómo solucionar esa limitación de lenguaje?
Rob Kennedy
Pensé que había algún tipo de forma especial de hacerlo que no conozco, sin tener que usar el constructor base.
amrhassan
Los miembros de la clase base ya están inicializados cuando se ejecuta el constructor de la clase derivada. Puede asignarlos , si tiene acceso, o establecer establecedores de llamadas para ellos, o puede proporcionar valores para ellos al constructor de la clase base, si hay uno adecuado. Lo único que no puede hacer en la clase diseñada es inicializarlos.
Marqués de Lorne

Respuestas:

143

No se puede inicializar ay ben Bporque no son miembros de B. Son miembros de A, por lo tanto, solo Apueden inicializarlos. Puede hacerlos públicos y luego realizar la asignación B, pero esa no es una opción recomendada ya que destruiría la encapsulación. En su lugar, cree un constructor Apara permitir B(o cualquier subclase de A) inicializarlos:

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.
    {
    } 
};
En silico
fuente
32
si bien su ejemplo es correcto, su explicación es engañosa. No es que no se puede es inicializar a y ben B::B()porque son privadas. No puede inicializarlos porque no son miembros de class B. Si los hizo públicos o los protegió, podría asignarlos en el cuerpo de B::B().
R Samuel Klatchko
2
Además, su solución hace que la clase A no sea agregada, lo que podría ser importante, por lo que debe mencionarse.
Gene Bushuyev
1
@R Samuel Klatchko: Buen punto. Cuando estaba escribiendo la respuesta, inicialmente escribí "No puede acceder ay b..." y lo cambié a "No puede inicializar ..." sin asegurarme de que el resto de la oración tuviera sentido. Publicación editada.
In silico
1
@Gene Bushuyev: La clase en el código original en la pregunta no es un agregado (hay miembros privados no estáticos)
David Rodríguez - dribeas
@David: correcto, que es un error del usuario, y estoy tratando de llegar a las intenciones del usuario, saltándome superficialmente.
Gene Bushuyev
26

Dejando de lado el hecho de que son private , ya que ay bson miembros de A, están destinados a ser inicializados por Alos constructores de ', no por los constructores de alguna otra clase (derivados o no).

Tratar:

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

De alguna manera, nadie enumeró la forma más sencilla:

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

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

};

No puede acceder a los miembros base en la lista de inicializadores, pero el propio constructor, al igual que cualquier otro método miembro, puede acceder publicy protectedmiembros de la clase base.

Jirafa violeta
fuente
1
Agradable. ¿Existe algún inconveniente en hacerlo de esta manera?
Wander3r
2
@SaileshD: puede haberlo, si está inicializando un objeto con un constructor costoso. Primero se inicializará por defecto cuando Bse asigne la instancia de , luego se asignará dentro del Bconstructor de. Pero también creo que el compilador aún puede optimizar esto.
Jirafa violeta
1
En el interior class Ano podemos confiar ay bestar inicializados. Cualquier implementación de class C : public A, por ejemplo, podría olvidarse de llamar a=0;y dejar sin ainicializar.
Sparkofska
@Sparkofska, muy cierto. Es mejor inicializar por defecto los campos ya sea en el lugar al declararlos ( class A { int a = 0;};), o en el constructor de la clase base. Las subclases aún pueden reinicializarlas en su constructor según sea necesario.
Jirafa violeta
1
@ Wander3r Otro inconveniente es que no todas las clases tienen operadores de asignación. Algunos solo pueden construirse, pero no asignarse. Entonces
terminaste
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;
}

Es un ejemplo práctico en caso de que desee inicializar los miembros de datos de la clase Base presentes en el objeto de la clase Derived, mientras que desea enviar estos valores a la interfaz a través de la llamada al constructor de la clase Derived.

manish srivastava
fuente
1

Si bien esto es útil en casos excepcionales (si ese no fuera el caso, el idioma lo habría permitido directamente), eche un vistazo al idioma Base from Member . No es una solución sin código, tendría que agregar una capa adicional de herencia, pero hace el trabajo. Para evitar el código repetitivo, puede usar la implementación de boost

Nikos Athanasiou
fuente
0

¿Por qué no puedes hacerlo? Porque el lenguaje no le permite inicializar los miembros de una clase base en la lista de inicializadores de la clase derivada.

¿Cómo puedes hacer esto? Me gusta esto:

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
fuente
-1

Si no especifica la visibilidad para un miembro de la clase, el valor predeterminado es "privado". Debe hacer que sus miembros sean privados o protegidos si desea acceder a ellos en una subclase.

TotoroTotoro
fuente
-1

Las clases agregadas, como A en su ejemplo (*), deben tener sus miembros públicos y no tener constructores definidos por el usuario. Se inicializan con la lista de inicializadores, por ejemplo, A a {0,0};o en su caso B() : A({0,0}){}. Los miembros de la clase agregada base no se pueden inicializar individualmente en el constructor de la clase derivada.

(*) Para ser precisos, como se mencionó correctamente, original class Ano es un agregado debido a miembros privados no estáticos

Gene Bushuyev
fuente