Héritage des constructeurs en C++

Lors de l'utilisation de l'héritage en C++, il est courant que la classe dérivée souhaite utiliser les constructeurs de la classe de base. Cependant, si la classe de base possède de nombreux constructeurs différents, il devient rapidemetn fastidieux de réécrire chaque constructeur dans la classe dérivée pour les transmettre. Par exemple :

struct ClasseBase
{
    ClasseBase(int entier) {}
    ClasseBase(double reel, int entier) {}
    ClasseBase(float flottant, int entier, const char* chaine) {}
    // ... diverses versions de constructeurs
};

struct ClasseDerivee : ClasseBase
{
    ClasseDerivee(int entier) : ClasseBase(entier) {}
    ClasseDerivee(double reel, int entier) : ClasseBase(reel, entier) {}
    ClasseDerivee(float flottant, int entier, const char* chaine) : ClasseBase(flottant, entier, chaine) {}
    // ... beaucoup de constructeurs correspondant à ceux de la classe de base
};

Cette approche devient très verbeuse lorsque la classe de base contient de nombreux constructeurs.

Héritage des fonctions membres

Dans le contexte de l'héritage, une fonction membre de la classe dérivée peut masquer une fonction membre de la classe de base si elles ont le même nom. Pour utiliser une fonction membre de la classe de base masquée, on peut utiliser une déclaration using. Par exemple :

struct ClasseBase
{
    void methode(int param) {}
};

struct ClasseDerivee : public ClasseBase
{
    using ClasseBase::methode;
    void methode(const char* buffer) {}
};

int main()
{
    ClasseDerivee objet;
    objet.methode(10); // Cette ligne produirait une erreur sans la déclaration using
}

Cette idée peut également être appliquée aux constructeurs :

struct ClasseBase
{
    ClasseBase(int entier) {}
    ClasseBase(double reel, int entier) {}
    ClasseBase(float flottant, int entier, const char* chaine) {}
    // ... diverses versions de constructeurs
};

struct ClasseDerivee : ClasseBase
{
    using ClasseBase::ClasseBase;
    // Une seule déclaration suffit pour hériter de tous les constructeurs
};

Avec using ClasseBase::ClasseBase;, tous les constructeurs de la classe de base sont hérités par la classe dérivée. De plus, si un constructeur hérité n'est pas utilisé, le compilateur n'en génère pas le code, ce qui permet de gagner de l'espace dans le code cible.

Cependant, une limitation est que les membres de la classe dérivée ne peuvent pas être initialisés par les constructeurs hérités. On peut tuotefois définir des valeurs par défaut pour les membres de la classe dérivée en utilisant des initialiseurs de membres. Par exemple :

struct ClasseBase
{
    ClasseBase(int entier) {}
    ClasseBase(double reel, int entier) {}
    ClasseBase(float flottant, int entier, const char* chaine) {}
};

struct ClasseDerivee : public ClasseBase
{
    using ClasseBase::ClasseBase;
    float membreFlottant = 10.0f; // Initialisation par défaut
};

Dans le cas de l'héritage multiple, si plusieurs classes de base possèdent des constructeurs avec les mêmes types de paramètres, cela peut entraîner des conflits d'héritage des constructeurs. Par exemple :

struct ClasseA { ClasseA(int entier) {} };

struct ClasseB { ClasseB(int entier) {} };

struct ClasseC : public ClasseA, public ClasseB
{
    using ClasseA::ClasseA;
    using ClasseB::ClasseB;
};

Pour résoudre ce problème, il est possible de définir explicitement les constructeurs conflictuels dans la classe dérivée. Par exemple :

struct ClasseC : public ClasseA, public ClasseB
{
    using ClasseA::ClasseA;
    using ClasseB::ClasseB;
    ClasseC(int) {} // Définition explicite
};

Quelques points importants à retenir :

  • Lorsque des constructeurs sont hérités, le compilateur ne génère pas de constructeur par défaut pour la classe dérivée.
  • Si un constructeur de la classe de base est privé ou si la classe dérivée hérite virtuellement de la classe de base, il est impossible de déclarer des constructeurs hérités.
class ClasseA
{
    ClasseA(int valeur) {}
};

class ClasseB : public ClasseA
{
public:
    using ClasseA::ClasseA;
};

int main()
{
    ClasseB objet; // Erreur : le constructeur par défaut de "ClasseB" est supprimé
}

Étiquettes: C++ Héritage constructeurs initialisation multiplicité

Publié le 28 août à 20h32