Le polymorphisme, un concept fondamental en programmation orientée objet (POO), joue un rôle crucial dans la conception de logiciels robustes et flexibles en C++. Ce concept permet à des objets de différentes classes d’être traités de manière uniforme par le biais de leur interface commune. En C++, le polymorphisme peut être réalisé de deux manières principales : le polymorphisme par sous-typage (ou polymorphisme d’héritage) et le polymorphisme par paramètre (ou polymorphisme d’interface).
Le polymorphisme par sous-typage est souvent associé à l’héritage de classes. Il permet à une classe dérivée de se comporter comme sa classe de base, tout en conservant la possibilité d’implémenter ses propres fonctionnalités. Cela signifie qu’un objet d’une classe dérivée peut être utilisé partout où un objet de sa classe de base est attendu, ce qui facilite la substitution d’objets et la gestion de collections d’objets hétérogènes.

Dans le contexte de C++, le polymorphisme par sous-typage est souvent mis en œuvre à l’aide de fonctions virtuelles. Une fonction virtuelle est une fonction déclarée dans une classe de base et redéfinie dans une classe dérivée. Lorsque vous appelez cette fonction à travers un pointeur ou une référence vers la classe de base, C++ sélectionne dynamiquement la fonction appropriée à appeler en fonction du type réel de l’objet.
Voici un exemple illustrant le polymorphisme par sous-typage en C++ :
cpp#include
class Forme {
public:
virtual void dessiner() const {
std::cout << "Dessiner une forme\n";
}
};
class Cercle : public Forme {
public:
void dessiner() const override {
std::cout << "Dessiner un cercle\n";
}
};
class Carre : public Forme {
public:
void dessiner() const override {
std::cout << "Dessiner un carré\n";
}
};
int main() {
Cercle cercle;
Carre carre;
Forme* forme1 = &cercle;
Forme* forme2 = &carre;
forme1->dessiner(); // Appelle dessiner() de Cercle
forme2->dessiner(); // Appelle dessiner() de Carre
return 0;
}
Dans cet exemple, les classes Cercle
et Carre
héritent de la classe de base Forme
. Chaque classe redéfinit la fonction dessiner()
. Ensuite, dans la fonction main()
, nous créons des objets de type Cercle
et Carre
, et nous les manipulons via des pointeurs de type Forme
. Lorsque nous appelons la fonction dessiner()
à travers ces pointeurs, le bon comportement polymorphique est observé, car les fonctions virtuelles sont résolues à l’exécution.
Le polymorphisme par paramètre, d’autre part, se concentre sur l’utilisation de classes abstraites ou d’interfaces pour définir un ensemble de méthodes communes sans implémentations. Les classes dérivées doivent alors fournir une implémentation pour ces méthodes communes, ce qui garantit que les objets de différentes classes peuvent être utilisés de manière interchangeable lorsqu’ils sont manipulés via une référence ou un pointeur vers l’interface commune.
En C++, le polymorphisme par paramètre est souvent réalisé à l’aide de classes abstraites et de fonctions virtuelles pures. Une fonction virtuelle pure est une fonction déclarée dans une classe de base sans fournir d’implémentation. Les classes dérivées doivent alors fournir une implémentation pour ces fonctions, sinon elles seront également considérées comme abstraites.
Voici un exemple illustrant le polymorphisme par paramètre en C++ :
cpp#include
class Animal {
public:
virtual void faireSon() const = 0; // Fonction virtuelle pure
};
class Chien : public Animal {
public:
void faireSon() const override {
std::cout << "Le chien aboie\n";
}
};
class Chat : public Animal {
public:
void faireSon() const override {
std::cout << "Le chat miaule\n";
}
};
void faireBruire(const Animal& animal) {
animal.faireSon();
}
int main() {
Chien chien;
Chat chat;
faireBruire(chien); // "Le chien aboie"
faireBruire(chat); // "Le chat miaule"
return 0;
}
Dans cet exemple, la classe Animal
définit une fonction virtuelle pure faireSon()
. Les classes Chien
et Chat
implémentent cette fonction. La fonction faireBruire()
prend un objet Animal
par référence constante et appelle sa méthode faireSon()
. Lorsque nous passons des objets de type Chien
et Chat
à cette fonction, le bon comportement polymorphique est observé, car les fonctions virtuelles pures sont implémentées dynamiquement par les classes dérivées.
En résumé, le polymorphisme en C++ offre une flexibilité et une extensibilité considérables dans la conception de logiciels orientés objet. En utilisant des concepts tels que les fonctions virtuelles et les classes abstraites, les programmeurs peuvent écrire un code plus modulaire, plus facile à maintenir et à étendre.
Plus de connaissances
Le polymorphisme en C++ est un pilier essentiel de la programmation orientée objet, offrant une flexibilité et une extensibilité considérables dans la conception de logiciels. En plus des deux principaux types de polymorphisme mentionnés précédemment, il existe d’autres aspects et techniques liés au polymorphisme en C++ qui méritent d’être explorés.
-
Polymorphisme avec des pointeurs et des références :
En C++, le polymorphisme peut être réalisé en manipulant des objets à travers des pointeurs ou des références. Cela permet de traiter des objets de classes dérivées de manière uniforme, en utilisant l’interface commune définie par la classe de base. Par exemple :cppForme* forme = new Cercle(); forme->dessiner(); // Appelle dessiner() de Cercle delete forme;
Ici, un objet de type
Cercle
est assigné à un pointeur de typeForme
, et l’appel de la méthodedessiner()
se fait de manière polymorphique. -
Polymorphisme avec des conteneurs :
Les conteneurs standard de la bibliothèque standard C++ peuvent également bénéficier du polymorphisme. Par exemple, vous pouvez stocker des objets de différentes classes dérivées dans un même conteneur, tel qu’un vecteur ou une liste, en utilisant des pointeurs ou des références vers la classe de base.cppstd::vector
formes; formes.push_back(new Cercle()); formes.push_back(new Carre()); for (auto& forme : formes) { forme->dessiner(); // Appelle dessiner() de Cercle et Carre polymorphiquement delete forme; } -
Polymorphisme avec des fonctions templates :
Les fonctions templates peuvent également bénéficier du polymorphisme. En écrivant des fonctions templates qui prennent des types génériques ou des objets polymorphiques en paramètres, vous pouvez réaliser du polymorphisme statique. Par exemple :cpptemplate<typename T> void afficherSon(const T& animal) { animal.faireSon(); // Appelle la méthode faireSon() de manière polymorphique } afficherSon(chien); // Appelle la version appropriée en fonction du type réel de l'objet afficherSon(chat);
-
Polymorphisme et destructeurs virtuels :
Lorsque vous utilisez l’héritage et le polymorphisme en C++, il est important de déclarer le destructeur de la classe de base comme virtuel. Cela garantit que les destructeurs des classes dérivées sont appelés correctement lorsqu’un objet est détruit via un pointeur ou une référence vers la classe de base.cppclass Forme { public: virtual ~Forme() {} // Destructeur virtuel };
Avec un destructeur virtuel, lorsqu’un objet de classe dérivée est détruit via un pointeur ou une référence vers la classe de base, le destructeur de la classe dérivée est appelé correctement, même si le pointeur ou la référence est de type de base.
En utilisant judicieusement ces techniques, les programmeurs peuvent exploiter pleinement le pouvoir du polymorphisme en C++ pour créer des systèmes logiciels robustes, modulaires et extensibles. Cependant, il est important de comprendre les implications et les bonnes pratiques associées à chaque approche pour éviter les problèmes de performances, de fuites de mémoire et de conception.