Contexte de la question:
C++ a introduit le support de la programmation orientée objet, fondamental pour les langages modernes. Pour réaliser le polymorphisme, des fonctions virtuelles ont été utilisées. Cela permettait d'appeler la bonne implémentation d'une méthode au moment de l'exécution, et non seulement à la compilation, ce qui est critique pour l'architecture avec héritage.
Problème:
Une erreur courante est la confusion entre l'appel de méthodes statiques et dynamiques, des destructeurs virtuels oubliés, et un mauvais usage de l'héritage (par exemple, le slicing d'objet, l'appel de la version de base au lieu de l'override). On confond souvent quand le polymorphisme fonctionne réellement.
Solution:
Une fonction virtuelle est déclarée à l'aide du mot-clé virtual dans la classe de base et peut être redéfinie (override) dans la classe dérivée. Si on appelle la fonction via un pointeur ou une référence à la classe de base, la version de la classe dérivée sera exécutée.
Exemple de code:
struct Base { virtual void foo() { std::cout << "Base::foo "; } }; struct Derived : Base { void foo() override { std::cout << "Derived::foo "; } }; void call(Base& b) { b.foo(); } int main() { Derived d; call(d); // Affichera Derived::foo }
Caractéristiques clés :
Le polymorphisme fonctionne-t-il lors du passage d'un objet par valeur ?
Non. Le passage par valeur entraîne un "slicing" — seule la partie correspondant au type du paramètre est copiée (généralement la classe de base), le polymorphisme est désactivé.
Exemple de code :
void call(Base b) { b.foo(); } // toujours appel de Base::foo
Faut-il déclarer le destructeur virtuel dans la classe de base ?
Oui, si l'on prévoit la suppression d'objets dérivés via un pointeur sur la classe de base. Sinon, cela entraînera une fuite de mémoire ou une non-libération des ressources.
Exemple de code :
struct Base { virtual ~Base() {} };
Que se passera-t-il si on n'utilise pas le mot-clé override dans la classe dérivée ?
Si dans la classe dérivée on ne spécifie pas override, mais qu'on modifie par erreur la signature de la fonction (par exemple, en omettant const ou en se trompant dans les paramètres), la fonction ne redéfinit pas la fonction virtuelle, une nouvelle est créée, et le polymorphisme ne fonctionne pas comme prévu.
Le programmeur n'a pas déclaré le destructeur de la classe de base comme virtuel ; la suppression d'un tableau d'objets via un pointeur de base a entraîné une fuite de mémoire.
Avantages :
Inconvénients :
Le destructeur virtuel a été déclaré ; seules des références/pointeurs au type de base ont été utilisés. Le polymorphisme a fonctionné correctement.
Avantages :
Inconvénients :