Como posso inicializar variáveis ​​de membro da classe base no construtor de classe derivada?

123

Por que não posso fazer isso?

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

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

};
Amrhassan
fonte
7
Você está perguntando por que não pode fazer isso, o que é uma questão de design de linguagem, ou está perguntando como contornar essa limitação da linguagem?
Rob Kennedy,
Pensei que houvesse uma forma especial de fazer isso que não conheço, sem precisar usar o construtor base.
amrhassan,
Os membros da classe base já estão inicializados no momento em que seu construtor de classe derivada começa a ser executado. Você pode atribuí- los, se tiver acesso, ou chamar setters para eles, ou pode fornecer valores para eles ao construtor da classe base, se houver um adequado. A única coisa que você não pode fazer na classe criada é inicializá-los.
Marquês de Lorne

Respostas:

143

Você não pode inicializar ae bem Bporque eles não são membros B. Eles são membros de A, portanto, apenas Apodem inicializá-los. Você pode torná-los públicos e, em seguida, fazer a atribuição B, mas essa não é uma opção recomendada, pois destruiria o encapsulamento. Em vez disso, crie um construtor em Apara permitir B(ou qualquer subclasse de A) inicializá-los:

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.
    {
    } 
};
Em sílico
fonte
32
embora seu exemplo esteja correto, sua explicação é enganosa. Não que você não pode é inicializar a e bem B::B()porque eles são privados. Você não pode inicializá-los porque eles não são membros de class B. Se você os tornar públicos ou protegidos, poderá atribuí- los no corpo de B::B().
R Samuel Klatchko
2
além disso, sua solução torna a classe A não agregada, o que pode ser importante, por isso deve ser mencionado.
Gene Bushuyev
1
@R Samuel Klatchko: Bom ponto. Quando estava escrevendo a resposta, inicialmente digitei "Você não pode acessar ae b..." e mudei para "Você não pode inicializar ..." sem ter certeza de que o resto da frase fazia sentido. Postagem editada.
In silico
1
@Gene Bushuyev: A classe no código original em questão não é um agregado (há membros privados não estáticos)
David Rodríguez - dribeas
@David - correto, que é um erro do usuário, e estou tentando chegar às intenções do usuário, pulando superficialmente.
Gene Bushuyev
26

Deixando de lado o fato de que eles são private, uma vez ae bsão membros A, eles são feitos para ser inicializado por Aconstrutores s, não por construtores de alguma outra classe (derivado ou não).

Experimentar:

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

De alguma forma, ninguém listou a maneira mais simples:

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

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

};

Você não pode acessar membros base na lista de inicializadores, mas o próprio construtor, assim como qualquer outro método de membro, pode acessar publice protectedmembros da classe base.

Girafa violeta
fonte
1
Agradável. Existe alguma desvantagem em fazer dessa maneira?
Wander3r
2
@SaileshD: pode haver, se você estiver inicializando um objeto com um construtor caro. Ele será inicializado por padrão quando a instância de Bfor alocada e, em seguida, será atribuído dentro do Bconstrutor de. Mas também acho que o compilador ainda pode otimizar isso.
Violet Giraffe
1
Por dentro class A, não podemos confiar ae bser inicializados. Qualquer implementação de class C : public A, por exemplo, pode se esquecer de chamar a=0;e deixar anão inicializado.
Sparkofska
@Sparkofska, muito verdadeiro. É melhor inicializar os campos por padrão no local ao declará-los ( class A { int a = 0;};) ou no construtor da classe base. As subclasses ainda podem reinicializá-los em seu construtor conforme necessário.
Violet Giraffe
1
@ Wander3r Outra desvantagem é que nem todas as classes têm operadores de atribuição. Alguns só podem ser construídos, mas não atribuídos. Então está feito ...
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;
}

É um exemplo de trabalho no caso de você desejar inicializar os membros de dados da classe Base presentes no objeto da classe Derived, enquanto você deseja enviar esses valores de interface por meio da chamada do construtor da classe Derived.

Manish Srivastava
fonte
1

Embora isso seja útil em casos raros (se não fosse o caso, o idioma teria permitido diretamente), dê uma olhada na Base de idioma dos membros . Não é uma solução livre de código, você teria que adicionar uma camada extra de herança, mas dá conta do recado. Para evitar o código clichê, você pode usar a implementação de boost

Nikos Athanasiou
fonte
0

Por que você não pode fazer isso? Porque a linguagem não permite que você inicialize os membros da classe base na lista de inicializadores da classe derivada.

Como você pode fazer isso? Como isso:

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

Se você não especificar a visibilidade para um membro da classe, o padrão é "privado". Você deve tornar seus membros privados ou protegidos se quiser acessá-los em uma subclasse.

TotoroTotoro
fonte
-1

Classes agregadas, como A em seu exemplo (*), devem ter seus membros públicos e não ter construtores definidos pelo usuário. Eles são inicializados com a lista de inicializadores, por exemplo, A a {0,0};ou no seu caso B() : A({0,0}){}. Os membros da classe agregada básica não podem ser inicializados individualmente no construtor da classe derivada.

(*) Para ser preciso, como foi corretamente mencionado, o original class Anão é um agregado devido a membros privados não estáticos

Gene Bushuyev
fonte