© FOTOGRIN/Shutterstock.com
En tant que langage de programmation orienté objet, C++ s’appuie fréquemment sur l’implémentation de l’héritage. Cette fonctionnalité vous permet de modifier et de réutiliser les attributs et autres caractéristiques d’une classe, ainsi que de réduire la quantité de code dont vous avez besoin. Lisez la suite pour découvrir exactement ce qu’est l’héritage et les types d’héritage en C++ que vous pouvez utiliser.
Qu’est-ce que l’héritage ?
De la même manière que nous pouvons”hériter”de certaines caractéristiques de nos parents, en C++, les classes peuvent hériter des membres d’une autre classe. De cette manière, la classe dont les membres sont hérités est définie comme classe de base ou parent, et la classe qui hérite de ces fonctionnalités est appelée classe dérivée, sous-classe ou classe enfant. Comme cela permet aux membres de la classe parent d’être réutilisés, ces membres n’ont pas besoin d’être redéfinis. Par conséquent, vous pouvez gagner du temps en implémentant du code inutile.
Il existe des similitudes avec la fonction friend et la classe friend, car elles permettent aux fonctions et aux classes d’accéder aux membres d’autres classes. Cependant, friend autorise l’accès aux membres privés, contrairement à l’héritage.
Quels sont les types d’héritage en C++ ?
Il existe 5 types d’héritage en C++. Ce sont :
Héritage uniqueHéritage à plusieurs niveauxHéritage multipleHéritage hybrideHéritage hiérarchique
Regardons chacun d’entre eux.
Héritage unique
La méthode d’héritage la plus simple est celle où une classe hérite des membres d’une autre classe. Cela peut être affiché avec le code suivant:
#include class BaseClass { public: int x; } ; class DerivedClass : public BaseClass{ public : int y ; } ; int main() { DerivedClass obj ; obj.x=10 obj.y=20 std::cout”Variable de membre de classe de base x=”std::endl; std::cout”Variable de membre de classe dérivée y=”obk.y std::endl; renvoie 0 ; }
Ici, nous avons défini la classe de base comme”BaseClass”, avec le membre”x”qui a un accès public. La classe dérivée est définie comme”DerivedClass”, qui hérite publiquement de”BaseClass”. La classe dérivée elle-même a un membre public appelé”y”. Nous créons une instance de la classe dérivée appelée”DerivedClass obj”et définissons les valeurs x et y. À l’aide de cet objet, nous accédons au membre”x”de la classe parent, et les valeurs x et y sont imprimées sur la console comme indiqué dans la capture d’écran.
Héritage unique illustré dans un exemple.
©”TNGD”.com
Hériter des membres privés
Il convient de noter que la classe dérivée peut hériter de tous les membres de la classe parent car ils ont un accès public. Cependant, si les membres sont déclarés privés, ils ne peuvent pas être hérités directement. Un moyen de contourner cela est si certaines fonctions qui agissent sur ces membres sont héritées. De cette façon, vous pouvez accéder indirectement à ces membres. Prenons l’exemple suivant:
class Base{ private: int a; public : Base(int a) a(a) {} int getA() const { return a ; } } ; class Derived: public Base { public: Derived(int a): Base(A) {} }; int main() { Dérivé d(5); std::cout”Valeur de l’objet dérivé:”d.getA() std::endl; renvoie 0 ; }
Ici, nous avons défini une classe parent comme”Base”et une classe dérivée comme”Derived”. Le membre”a”de la classe parent est privé, donc pas directement accessible par la classe dérivée. Mais, nous avons un constructeur dans la classe de base qui prend l’argument entier de”a”et définit le membre privé”a”. La classe dérivée a un constructeur lui-même,”Derived(int a): Base (a) {}”qui prend l’argument entier”a”et le passe au constructeur de la classe de base. Comme la fonction”getA()”a été héritée, un accès indirect au membre”a”de la classe parent est obtenu.
En utilisant la fonction”main”, nous prenons une instance”d”de la classe dérivée et définissez-la avec un argument de 5. La fonction”getA()”est appelée sur cette instance et la sortie est imprimée. Ceci est montré dans la capture d’écran.
Comment hériter des membres privés, illustré dans un exemple.
©”TNGD”.com
Classes privées vs classes protégées
Il serait utile à ce stade de faire la distinction entre classes privées et classes protégées. Alors que les membres de classe privés ne sont accessibles qu’indirectement par les classes dérivées, les membres de classe protégés sont entièrement accessibles par une classe dérivée. Mais ils ne sont accessibles qu’au sein de cette hiérarchie de classes, et non en dehors de celle-ci (il s’agirait d’un accès public).
Héritage de méthode uniquement
Ces exemples ont également montré l’héritage de champ, où le champ”a”a été hérité. Mais vous pouvez également avoir un héritage de méthode uniquement, où une méthode est utilisée pour définir le comportement de la classe. Un exemple de ceci est montré ci-dessous:
#inlude class Base { public: virtual void bar() { std::cout”Base::bar()”std::endl; } } ; class Derived: public Base { public: virtual voide bar() { std::cout”Derived::bar()”std::endl; } } ; int main() { Base* b=new Derived(); b->bar();//affiche”Derived::bar()”delete b ; renvoie 0 ; }
Comme avant, nous avons une classe parente”Base”et une classe dérivée”Derived”.”Base”a une méthode virtuelle”bar()”. Ceci est appelé virtuel car il n’a pas d’implémentation dans la classe parente et peut être remplacé par la classe dérivée. L’implémentation de”bar()”est fournie dans la définition de la classe dérivée. On peut voir qu’un pointeur « b » est créé au sein de la fonction « main() », et initialisé avec l’objet « Derived ».”Derived::bar()”remplace”Base::bar()”, puisqu’il s’agit d’une fonction virtuelle. L’implémentation dans la classe dérivée est appelée, ce qui imprime la sortie”Derived:: bar ()”comme indiqué dans la capture d’écran.
Un exemple montrant l’héritage de méthode uniquement.
©”TNGD”.com
Héritage à plusieurs niveaux
Il existe de nombreux cas où vous souhaiterez créer une classe dérivée à partir d’une classe qui a déjà été dérivée d’une classe parent. C’est ce qu’on appelle l’héritage multiniveau. En ce qui concerne les types d’héritage en C++, celui-ci est assez intuitif. Un tel exemple est lorsque vous travaillez avec des types de véhicules. Dans cette situation, vous pouvez avoir une classe parente de”Véhicules”qui définit certaines méthodes et paramètres universels pour les véhicules avec lesquels vous travaillez, tels que les voitures et les motos. Celles-ci peuvent toutes deux être des classes dérivées de cette classe parent, avec leurs propres propriétés spécifiques. Mais comme vous pouvez en avoir des types particuliers, vous pouvez alors créer des classes dérivées comme”PerformanceCar”qui auraient alors leurs propres propriétés applicables.
Nous pouvons illustrer comment cela fonctionne avec le code suivant :
#include #include class Véhicule { public: std::string type; std:: couleur de chaîne ; } ; class Car: public Vehicle { public: std::string model; int année ; } ; class ElectricCar: public Car { public: int batteryCapacity; } ; int main() { ElectricCar tesla; tesla.type=”Voiture électrique” ; tesla.color=”Rouge”; tesla.model=”Modèle S” ; tesla.année=2021; tesla.batteryCapacity=100 ; std::cout”Type:”tesla.type std::endl; std::cout”Color:”tesla.color std::endl; std:: cout”Modèle:”tesla.model std:: endl; std::cout”Année :”tesla.year std::endl ; std:: cout”Capacité de la batterie :”tesla.batteryCapacity std:: endl ; renvoie 0 ; }
Dans cet exemple, nous avons la classe de base”Vehicle”, la classe dérivée”Car”, et une autre classe dérivée appelée”ElectricCar”. La classe”Vehicle”a les membres”type”et”color”, la classe”Car”a les membres”model”et la variable entière”year”, et la classe”ElectricCar”a la variable membre entière”batteryCapacity”. La fonction principale crée une instance de la classe”ElectricCar”, définissant toutes ses variables membres héritées avec des valeurs spécifiques. Ces variables sont ensuite imprimées sur la console.
Héritage multiniveau implémenté dans un exemple, avec sa sortie.
©”TNGD”.com
Héritage multiple
Tout comme nous pouvons avoir une classe dérivée à partir d’une classe déjà dérivée, nous pouvons avoir une classe dérivée de plusieurs classes de base. Nous pouvons le démontrer en continuant avec notre analogie avec la voiture :
#include #include class Vehicle { public: std::string type; std:: couleur de chaîne ; } ; class ElectricEngine { public : int batteryCapacity ; } ; class SportsCar { public : int topSpeed ; } ; class ElectricSportsCar: public Vehicle, public ElectricEngine, public SportsCar { public: std::string model; int année ; } ; int main() { tesla ElectricSportsCar; tesla.type=”Voiture électrique” ; tesla.color=”Rouge”; tesla.model=”Roadster” ; tesla.année=2022 ; tesla.batteryCapacity=200 ; tesla.topSpeed =250 ; std::cout”Type:”tesla.type std::endl; std::cout”Color:”tesla.color std::endl; std:: cout”Modèle:”tesla.model std:: endl; std::cout”Année :”tesla.year std::endl ; std::cout”Capacité de la batterie :”tesla.batteryCapacity”kWh”std::endl ; std::cout”Vitesse maximale :”tesla.topSpeed ”mph”std::endl ; renvoie 0 ; }
Nous avons trois classes ici,”Vehicle”,”ElectricEngine”et”SportsCar”. Ce sont toutes des classes de base, et leurs propriétés sont héritées par la classe dérivée”ElectricSportsCar”. Comme précédemment, chacune des classes mères a ses propres variables. Une instance de”ElectricSportsCar”est créée, des valeurs sont attribuées à ses propriétés et celles-ci sont ensuite imprimées sur la console.
Un exemple d’héritage multiple.
©”TNGD”.com
Héritage hiérarchique
Nous arrivons à la fin des différents types d’héritage en C++. C’est là que la situation est un peu plus nuancée. Bien que l’héritage multiniveau crée techniquement une hiérarchie, cela diffère de ce que l’on appelle l’héritage hiérarchique. Si vous créez plusieurs classes dérivées à partir d’une classe parent, cela s’appelle l’héritage hiérarchique. Ceci est utile lorsque vous devez créer des classes liées à partir d’une classe parent, mais chacune avec ses propres propriétés. Le code ci-dessous montre cet exemple.
class Vehicle { public: std::string type; std:: couleur de chaîne ; } ; class Car: public Vehicle { public: std:: string model; int année ; } ; class Truck: public Vehicle { public: int capacity; } ;
Encore une fois, nous avons la classe parente”Véhicule”, et les classes”Voiture”et”Camion”en sont dérivées. Ces deux classes héritent des variables”type”et”couleur”, mais ajoutent également leurs propres propriétés. A savoir, « modèle » et « année » pour la classe « Voiture » et « capacité » pour la classe « Camion ». Dans la première capture d’écran, nous pouvons voir le code implémenté avec la fonction”main()”, avec une instance de chaque classe créée et ses attributs définis. La deuxième capture d’écran montre la sortie dans la console.
Héritage hiérarchique implémenté dans une classe.
©”TNGD”.com
Le résultat de l’exemple ci-dessus.
©”TNGD”.com
Héritage hybride
La situation devient un peu plus complexe en ce qui concerne l’héritage hybride. C’est là que différents types d’héritage en C++ sont combinés dans une hiérarchie de classes. Bien que cela puisse devenir complexe, cela est parfois nécessaire pour les opérations que vous devez effectuer. Considérez la situation suivante:
#include #include class Vehicle { public: std::string type; std:: couleur de chaîne ; } ; moteur de classe { public : int chevaux-vapeur ; } ; class Car : public Vehicle, public Engine { public : std::string model ; int année ; } ; class ElectricCar: public Car { public: int batteryCapacity; } ; dans main() { ElectricCar tesla; tesla.type=”Voiture électrique” ; tesla.color=”Rouge”; tesla.model=”Modèle S” ; tesla.horsepower=300; tesla.année=2021; tesla.batteryCapacity=100 ; tesla.type=”Voiture électrique” ; std::cout”Type:”tesla.type std::endl; std::cout”Color:”tesla.color std::endl; std:: cout”Puissance :”tesla.horsepower std:: endl; std:: cout”Modèle:”tesla.model std:: endl; std::cout”Année :”tesla.year std::endl ; std:: cout”Capacité de la batterie :”tesla.batteryCapacity std:: endl ; renvoie 0 ; }
Comme précédemment, nous avons la classe parente”Vehicle”, mais aussi la classe parente”Engine”. Nous avons un exemple d’héritage multiple, où la classe”Voiture”est dérivée à la fois des classes”Véhicule”et”Moteur”. La classe « ElectricCar » est alors dérivée de la classe « Car », qui est un exemple d’héritage à plusieurs niveaux, car il s’agit d’un type de voiture plus spécialisé. Par conséquent, nous avons un scénario où l’héritage hybride a lieu. En tant que tel, l’héritage hybride peut être utile pour simplifier et comprendre les relations complexes, ainsi que pour faciliter la maintenance du code.
La première image illustre l’implémentation du code, où nous avons créé une instance du Classe”ElectricCar”avec son attribut défini à partir de toutes ses classes parentes. La sortie imprimée est affichée dans la deuxième image.
Héritage hybride implémenté dans une classe.
©”TNGD”.com
Le résultat de l’exemple ci-dessus.
©”TNGD”.com
Une note sur l’ambiguïté
Un problème très courant qui peut survenir lorsqu’il s’agit de plusieurs classes est la question de l’ambiguïté. C’est là que vous avez des variables membres ou des fonctions identiques dans deux classes ou plus. Le compilateur peut alors rencontrer des erreurs, car il ne peut pas décider quel membre utiliser pour l’opération que vous essayez d’exécuter. Considérez le code suivant :
#include class A { public: void foo() {std::cout”A:foo()”std::endl;} }; classe B { public: void foo() {std::cout”B::foo()”std::endl;} } ; classe C: public A, public B { public: } ; int principal() { C c; c.foo();//Erreur du compilateur : appel ambigu à foo() de A et B renvoie 0 ; }
Ici, nous avons une fonction nommée”foo()”dans les classes”A”et”B”. La classe « C » ayant hérité de ces deux classes, une erreur se produit lors de la compilation. Cela peut être résolu de différentes manières :
Renommer la fonction ou la variable en conflit dans la classe parente ou dérivée pour éviter toute confusion. Utiliser l’opérateur de résolution de portée (::) pour spécifier la classe contenant la fonction membre souhaitée. L’héritage virtuel fonctionne sur une notion similaire à la fonction virtuelle mentionnée précédemment. Cela peut permettre à une classe dérivée d’hériter des classes parentes sans dupliquer la classe de base que ces parents partagent. Remplacer la fonction conflictuelle en redéfinissant la fonction dans la classe dérivée avec une implémentation spécialisée. Une erreur d’ambiguïté illustrée.
©”TNGD”.com
Récapitulatif
Il existe 5 principaux types d’héritage en C++ que vous pouvez utiliser, chacun avec ses propres cas d’utilisation spécifiques. Bien que l’héritage unique puisse convenir à des opérations relativement simples, lorsque vous travaillez avec des objets et des relations complexes, d’autres types tels que l’héritage multiple, multiniveau, hiérarchique et hybride peuvent être plus appropriés. Ceux-ci peuvent être utiles pour rendre le code plus intuitif et plus facile à maintenir, ainsi que pour mieux représenter la relation entre les classes.
L’héritage en C++ expliqué, avec des exemples FAQ (Foire aux questions)
Que sont les classes parentes et les classes dérivées ?
Une classe parente, également appelée classe de base, est la classe dont elle hérite. D’autre part, une classe dérivée, une sous-classe ou une classe enfant hérite des propriétés et des attributs de la classe parent.
Qu’est-ce que l’héritage en C++ ?
L’héritage est un processus qui vous permet de créer une classe dérivée à partir d’une classe parent ou de base, qui hérite des propriétés et des fonctions membres de la classe parent. Cela peut être utile pour représenter des relations compliquées et réduire le besoin de répéter l’implémentation du code.
Quels sont les différents types d’héritage en C++ ?
Il existe 5 principaux types d’héritage en C++-simple, multiple, multiniveaux, hiérarchique et hybride. Unique et multiple font référence à une classe unique dérivée respectivement d’une ou de plusieurs classes de base. Les héritages à plusieurs niveaux signifient qu’une classe est dérivée d’une classe dérivée. L’héritage hiérarchique fait référence au moment où plusieurs classes sont dérivées d’une classe de base, et l’héritage hybride signifie qu’un mélange de ces autres types est utilisé.
Comment implémentez-vous l’héritage en C++ ?
Pour faire hériter une classe d’une classe parent, vous utilisez deux points (:), suivis du spécificateur d’accès (public ou protégé dans ce cas) et du nom de la classe de base.
Que sont les membres publics, protégés et privés ?
Les membres publics sont entièrement visibles et accessibles de n’importe où dans un programme, et peuvent être consultés par n’importe quelle fonction à l’intérieur ou à l’extérieur de la classe. Les membres protégés sont accessibles par les classes dérivées et peuvent donc être hérités. Les membres privés ne sont accessibles par aucune fonction en dehors de la classe et sont invisibles pour les classes dérivées. Cependant, les membres privés peuvent l’être indirectement si une fonction publique qui agit sur eux est héritée.
Quelle est la différence entre l’héritage public, privé et protégé ?
L’héritage public est l’endroit où tous les membres publics de la classe de base deviennent des membres publics de la classe dérivée, et de même avec les membres protégés et privés. L’héritage protégé est l’endroit où les membres publics et protégés sont hérités, mais les membres privés restent inaccessibles. L’héritage privé signifie que les membres publics et protégés deviennent des membres privés de la classe dérivée et, comme toujours, les membres privés restent invisibles.
Comment résolvez-vous l’ambiguïté dans l’héritage ?
Vous pouvez résoudre l’ambiguïté en utilisant le générateur de résolution de portée, en utilisant l’héritage virtuel ou en renommant ou en redéfinissant les fonctions en conflit.