Héritage en C++ : comment appeler correctement le constructeur de la classe de base sans casser le code

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction : pourquoi les constructeurs des classes parentes sont un champ de mines pour les débutants



Quiconque a commencé à étudier l'héritage en C++ a tôt ou tard rencontré une situation : vous créez une classe dérivée, le compilateur râle, le programme ne compile pas, et vous ne comprenez pas pourquoi ? Le plus souvent, la racine du mal réside dans un appel incorrect du constructeur de la classe de base. Cette question sur Stack Overflow a rassemblé des milliers de vues, et ce n'est pas pour rien : même les développeurs expérimentés oublient parfois les règles élémentaires.



Dans cet article, nous allons analyser les règles d'appel du constructeur de la classe de base en C++, passer en revue le top 5 des erreurs les plus courantes et montrer comment les éviter. Si vous voulez écrire du code fiable sans surprises, lisez jusqu'à la fin.



Petit rappel : comment fonctionnent les constructeurs lors de l'héritage



En C++, les constructeurs ne sont pas hérités. Lorsque vous créez un objet d'une classe dérivée, le constructeur de la classe de base est appelé en premier (implicitement ou explicitement), puis celui du dérivé. Si vous ne spécifiez pas quel constructeur de la classe parente appeler, le compilateur tentera d'appeler le constructeur par défaut de la classe de base. S'il n'existe pas, vous obtiendrez une erreur de compilation.



L'appel explicite se fait via la liste d'initialisation :



class Base {public:    Base(int x) { /* ... */ }};

class Derived : public Base {public: // Correct : appel explicite du constructeur Base(int) Derived() : Base(42) { /* ... */ }};


Cela semble simple ? Mais c'est précisément ici que les débutants commettent 90 % des erreurs. Passons-les en revue dans l'ordre.



Top 5 des erreurs lors de l'appel du constructeur de la classe de base



Erreur n°1 : Oubli d'appeler le constructeur de la classe parente avec paramètres



Le problème le plus courant. Si la classe de base n'a pas de constructeur par défaut et que vous ne l'appelez pas explicitement, le compilateur générera une erreur.



class Base {public:    Base(int x) { /* initialisation */ }};

class Derived : public Base {public: // ERREUR : le compilateur tente d'appeler Base(), qui n'existe pas Derived() { }};


Comment corriger :



class Derived : public Base {public:    // CORRECT : appel explicite de Base(int)    Derived() : Base(0) { }    // Ou passage d'un paramètre externe :    Derived(int x) : Base(x) { }};


Erreur n°2 : Ordre d'initialisation incorrect



Beaucoup pensent que l'ordre d'appel des constructeurs dans la liste d'initialisation détermine l'ordre d'exécution. Ce n'est pas le cas. La classe de base est toujours initialisée en premier, indépendamment de l'endroit où vous avez écrit son appel.



class Derived : public Base {private:    int value;public:    // On dirait que value est initialisé en premier ? Non !    Derived() : value(10), Base(5) { }    // En réalité : d'abord Base(5), puis value(10)};


Cela peut entraîner des erreurs logiques si l'initialisation de value dépend de l'état de la classe de base.



Erreur n°3 : Appel du constructeur parent dans le corps du constructeur



Certains débutants tentent d'appeler le constructeur de la classe de base à l'intérieur des accolades, comme une méthode ordinaire. Cela ne fonctionnera pas — le constructeur ne peut être appelé que dans la liste d'initialisation.



class Derived : public Base {public:    Derived() {        // ERREUR : le constructeur n'est pas appelé ainsi        Base(10); // Crée simplement un objet temporaire et le supprime    }};


Erreur n°4 : Confusion avec les constructeurs de copie



Lors de l'implémentation du constructeur de copie dans une classe dérivée, vous devez appeler explicitement le constructeur de copie de la classe de base. Sinon, le constructeur par défaut sera appelé et les données de la classe de base ne seront pas copiées.



class Derived : public Base {public:    // ERREUR : Base sera copiée incorrectement    Derived(const Derived& other) { /* ... */ }

// CORRECT : Derived(const Derived&

Blogs

Book Recommendations