Comment puis-je initialiser les variables de membre de classe de base dans le constructeur de classe dérivée?

123

Pourquoi je ne peux pas faire ça?

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

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

};
amrhassan
la source
7
Demandez-vous pourquoi vous ne pouvez pas faire cela, qui est une question de conception de langage, ou demandez-vous comment contourner cette limitation de langage?
Rob Kennedy
Je pensais qu'il y avait une sorte de façon spéciale de le faire dont je ne suis pas au courant, sans avoir à utiliser le constructeur de base.
amrhassan
Les membres de la classe de base sont déjà initialisés au moment où votre constructeur de classe dérivée s'exécute. Vous pouvez les affecter , si vous y avez accès, ou appeler des setters pour eux, ou vous pouvez fournir des valeurs pour eux au constructeur de classe de base, s'il y en a un qui convient. La seule chose que vous ne pouvez pas faire dans la classe conçue est de les initialiser.
Marquis of Lorne

Réponses:

143

Vous ne pouvez pas initialiser aet bin Bcar ils ne sont pas membres de B. Ils sont membres de A, donc seuls Apeuvent les initialiser. Vous pouvez les rendre publics, puis les attribuer B, mais ce n'est pas une option recommandée car cela détruirait l'encapsulation. Au lieu de cela, créez un constructeur Apour permettre B(ou une sous-classe de A) de les initialiser:

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
la source
32
bien que votre exemple soit correct, votre explication est trompeuse. Ce n'est pas que vous ne pouvez pas initialiser a et ben B::B()car ils sont privés. Vous ne pouvez pas les initialiser car ils ne sont pas membres de class B. Si vous les avez rendus publics ou protégés, vous pouvez les attribuer dans le corps de B::B().
R Samuel Klatchko
2
De plus, votre solution rend la classe A non agrégée, ce qui peut être important, il faut donc le mentionner.
Gene Bushuyev
1
@R Samuel Klatchko: Bon point. Quand j'écrivais la réponse, j'ai d'abord tapé "Vous ne pouvez pas accéder aet b..." et l'ai changé en "Vous ne pouvez pas initialiser ..." sans m'assurer que le reste de la phrase avait du sens. Message modifié.
In silico le
1
@Gene Bushuyev: La classe dans le code original de la question n'est pas un agrégat (il y a des membres privés non statiques)
David Rodríguez - dribeas
@David - correct, ce qui est une erreur de l'utilisateur, et j'essaie de comprendre les intentions de l'utilisateur, en sautant superficiel.
Gene Bushuyev
26

En laissant de côté le fait qu'ils sont private, depuis aet bsont membres de A, ils sont censés être initialisés par Ales constructeurs de, pas par les constructeurs d'une autre classe (dérivés ou non).

Essayer:

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
la source
7

D'une manière ou d'une autre, personne n'a énuméré le moyen le plus simple:

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

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

};

Vous ne pouvez pas membres de base accès dans la liste des initialiseur, mais le constructeur lui - même, comme toute autre méthode membre, peut accéder publicet les protectedmembres de la classe de base.

Girafe violette
la source
1
Agréable. Y a-t-il un inconvénient à procéder ainsi?
Wander3r
2
@SaileshD: il peut y en avoir, si vous initialisez un objet avec un constructeur coûteux. Il sera d'abord initialisé par défaut lorsque l'instance de Best allouée, puis elle sera affectée à l'intérieur du Bconstructeur de. Mais je pense aussi que le compilateur peut toujours optimiser cela.
Violet Giraffe
1
À l'intérieur, class Anous ne pouvons pas compter sur aet en bcours d'initialisation. Toute implémentation de class C : public A, par exemple, peut oublier d'appeler a=0;et ne pas être ainitialisée.
Sparkofska
@Sparkofska, très vrai. Il est préférable d'initialiser les champs par défaut soit sur place lors de leur déclaration ( class A { int a = 0;};), soit dans le constructeur de la classe de base. Les sous-classes peuvent toujours les réinitialiser dans leur constructeur si nécessaire.
Violet Giraffe
1
@ Wander3r Un autre inconvénient est que toutes les classes n'ont pas d'opérateurs d'affectation. Certains peuvent seulement être construits, mais pas assignés. Alors vous avez terminé ...
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;
}

C'est un exemple de travail dans le cas où vous souhaitez initialiser les membres de données de classe de base présents dans l'objet de classe dérivée, alors que vous souhaitez pousser ces valeurs en interface via un appel de constructeur de classe dérivée.

manish srivastava
la source
1

Bien que cela soit utile dans de rares cas (si ce n'était pas le cas, la langue l'aurait permis directement), jetez un œil à l' idiome Base from Member . Ce n'est pas une solution sans code, vous devrez ajouter une couche supplémentaire d'héritage, mais cela fait le travail. Pour éviter le code standard, vous pouvez utiliser l'implémentation de boost

Nikos Athanasiou
la source
0

Pourquoi tu ne peux pas le faire? Parce que le langage ne vous permet pas d'initialiser les membres d'une classe de base dans la liste d'initialisation de la classe dérivée.

Comment pouvez-vous y parvenir? Comme ça:

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
la source
-1

Si vous ne spécifiez pas de visibilité pour un membre de classe, la valeur par défaut est "privée". Vous devez rendre vos membres privés ou protégés si vous souhaitez y accéder dans une sous-classe.

TotoroTotoro
la source
-1

Les classes agrégées, comme A dans votre exemple (*), doivent avoir leurs membres publics et ne pas avoir de constructeurs définis par l'utilisateur. Ils sont initialisés avec la liste d'initialisation, par exemple A a {0,0};ou dans votre cas B() : A({0,0}){}. Les membres de la classe d'agrégation de base ne peuvent pas être initialisés individuellement dans le constructeur de la classe dérivée.

(*) Pour être précis, comme il a été correctement mentionné, l'original class An'est pas un agrégat en raison de membres privés non statiques

Gene Bushuyev
la source