ProgrammationDéveloppeur C++, architecte système

Qu'est-ce que le polymorphisme en C++ et comment est-il mis en œuvre dans la pratique ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Historique de la question :

Le polymorphisme est devenu l'une des caractéristiques clés de la programmation orientée objet dès le début du développement de C++. L'objectif est de pouvoir accéder aux objets via une interface de base, sans se soucier du type concret. Cela élargit considérablement l'expressivité et la flexibilité du code.

Problème :

Sans polymorphisme, le code devient rigide : il est nécessaire de connaître explicitement les types d'objets, d'utiliser switch/case, de faire des conversions de types manuelles. Cela complique la maintenance et l'évolutivité de l'application - l'ajout de nouveaux types devient coûteux ou impossible sans modifier le code existant.

Solution :

En C++, le polymorphisme est atteint par l'utilisation de fonctions virtuelles. Les classes déclarent des méthodes virtuelles, et leurs héritiers les réalisent. La classe de base fournit une interface commune, tandis que les actions réelles dépendent du type d'objet réel auquel pointe le pointeur ou la référence.

Exemple de code :

#include <iostream> class Animal { public: virtual void speak() const { std::cout << "Un bruit d'animal "; } virtual ~Animal() {} }; class Dog : public Animal { public: void speak() const override { std::cout << "Woof! "; } }; void makeSound(const Animal& a) { a.speak(); } int main() { Dog dog; makeSound(dog); // Affiche : Woof! }

Caractéristiques clés :

  • Les fonctions virtuelles doivent être déclarées avec le mot-clé virtual dans la classe de base.
  • Pour plus de sécurité, utilisez un destructeur virtuel dans une hiérarchie polymorphe.
  • Pour un remplacement explicite, utilisez override - cela augmente la sécurité du code.

Questions pièges.

Que se passe-t-il si le destructeur de la classe de base n'est pas déclaré virtuel ?

La suppression d'un objet via un pointeur vers la classe de base n'appellera que le destructeur de la classe de base, et le destructeur de la classe dérivée ne sera pas appelé, ce qui entraînera une fuite de ressources.

Exemple de code :

class Base { public: ~Base() { /*...*/ } }; class Derived : public Base { public: ~Derived() { /*...*/ } }; Base* obj = new Derived(); delete obj; // UB : Derived::~Derived ne sera pas appelé

Peut-on déclarer des méthodes partiellement virtuelles et laisser le destructeur non virtuel ?

Non, si la classe est polymorphe (il y a au moins une fonction virtuelle), le destructeur doit être virtuel pour éviter les fuites de mémoire ou de ressources.

Les fonctions virtuelles fonctionnent-elles pour les membres déclarés comme static ?

Non, les membres statiques de la classe ne peuvent pas être virtuels car ils n'appartiennent pas à un objet spécifique et il n'existe pas de mécanisme de liaison dynamique pour eux.

Erreurs typiques et anti-modèles

  • Absence de destructeur virtuel dans une hiérarchie avec des méthodes virtuelles.
  • Oubli de marquer le méthode override dans la classe dérivée, ce qui entraîne un remplacement erroné de la méthode.
  • Appels de fonctions virtuelles depuis des constructeurs/destructeurs, lorsque la liaison dynamique ne devrait pas exister.

Exemple de la vie réelle

Cas négatif

Une très grande hiérarchie de classes d'appareils, chaque classe dérivée gère sa propre ressource (par exemple, un fichier ouvert), mais le destructeur de la classe de base n'est pas virtuel. Lors de la suppression via un pointeur vers la base, les ressources ne sont pas libérées.

Avantages : Le projet se construit rapidement, un minimum d'appels virtuels.

Inconvénients : Fuites de mémoire, destruction incorrecte. Extrêmement difficile à maintenir et à faire évoluer.

Cas positif

Une hiérarchie polymorphe bien conçue, la classe de base a des fonctions virtuelles et un destructeur virtuel. Le mot-clé override et les principes de RAII sont utilisés.

Avantages : Gestion sécurisée des ressources, extension simple, testabilité.

Inconvénients : Performance légèrement inférieure en raison de la recherche dans la table virtuelle, vaccin contre "l'over-engineering" si l'héritage est appliqué sans nécessité.