Une fonction virtuelle (également appelée méthodes virtuelles) est une fonction membre déclarée dans une classe de base et redéfinie (remplacée) par une classe dérivée. Lorsque vous faites référence à un objet de classe dérivée à l’aide d’un pointeur ou d’une référence à la classe de base, vous pouvez appeler une fonction virtuelle pour cet objet et exécuter la version de la méthode de la classe dérivée.
- Les fonctions virtuelles garantissent que la fonction correcte est appelée pour un objet, quel que soit le type de référence (ou pointeur) utilisé pour l'appel de fonction.
- Ils sont principalement utilisés pour réaliser le polymorphisme d'exécution.
- Les fonctions sont déclarées avec un virtuel mot-clé dans une classe de base.
- La résolution d'un appel de fonction se fait au moment de l'exécution.
Règles pour les fonctions virtuelles
Les règles pour les fonctions virtuelles en C++ sont les suivantes :
- Les fonctions virtuelles ne peuvent pas être statiques.
- Une fonction virtuelle peut être une fonction amie d’une autre classe.
- Les fonctions virtuelles doivent être accessibles à l’aide d’un pointeur ou d’une référence de type classe de base pour obtenir le polymorphisme d’exécution.
- Le prototype des fonctions virtuelles doit être le même dans la base ainsi que dans la classe dérivée.
- Ils sont toujours définis dans la classe de base et remplacés dans une classe dérivée. Il n'est pas obligatoire que la classe dérivée remplace (ou redéfinisse la fonction virtuelle), dans ce cas, la version de classe de base de la fonction est utilisée.
- Une classe peut avoir un destructeur virtuel mais elle ne peut pas avoir de constructeur virtuel.
Comportement au moment de la compilation (liaison anticipée) VS à l'exécution (liaison tardive) des fonctions virtuelles
Considérez le programme simple suivant montrant le comportement d'exécution des fonctions virtuelles.
mot-clé final en Java
C++
np.log
// C++ program to illustrate> // concept of Virtual Functions> #include> using> namespace> std;> class> base {> public>:> >virtual> void> print() { cout <<>'print base class
'>; }> >void> show() { cout <<>'show base class
'>; }> };> class> derived :>public> base {> public>:> >void> print() { cout <<>'print derived class
'>; }> >void> show() { cout <<>'show derived class
'>; }> };> int> main()> {> >base* bptr;> >derived d;> >bptr = &d;> >// Virtual function, binded at runtime> >bptr->imprimer();> >// Non-virtual function, binded at compile time> >bptr->show();> >return> 0;> }> |
>
>Sortir
print derived class show base class>
Explication: Le polymorphisme d'exécution est obtenu uniquement via un pointeur (ou une référence) du type de classe de base. De plus, un pointeur de classe de base peut pointer vers les objets de la classe de base ainsi que vers les objets de la classe dérivée. Dans le code ci-dessus, le pointeur de classe de base « bptr » contient l'adresse de l'objet « d » de la classe dérivée.
La liaison tardive (au moment de l'exécution) est effectuée conformément au contenu du pointeur (c'est-à-dire l'emplacement pointé par le pointeur) et la liaison anticipée (au moment de la compilation) est effectuée en fonction du type de pointeur puisque la fonction print() est déclarée avec le virtuel mot-clé afin qu'il soit lié au moment de l'exécution (la sortie est classe dérivée d'impression car le pointeur pointe vers l'objet de la classe dérivée) et show() n'est pas virtuel donc il sera lié pendant la compilation (la sortie est afficher la classe de base car le pointeur est de type base).
python réduire
Note: Si nous avons créé une fonction virtuelle dans la classe de base et qu'elle est remplacée dans la classe dérivée, nous n'avons pas besoin d'un mot-clé virtuel dans la classe dérivée, les fonctions sont automatiquement considérées comme des fonctions virtuelles dans la classe dérivée.
Fonctionnement des Fonctions Virtuelles (concept de VTABLE et VPTR)
Comme indiqué ici, si une classe contient une fonction virtuelle, le compilateur lui-même fait deux choses.
- Si un objet de cette classe est créé, alors un pointeur virtuel (VPTR) est inséré en tant que donnée membre de la classe pour pointer vers la VTABLE de cette classe. Pour chaque nouvel objet créé, un nouveau pointeur virtuel est inséré en tant que donnée membre de cette classe.
- Que l'objet soit créé ou non, la classe contient comme membre un tableau statique de pointeurs de fonction appelé VTABLE . Les cellules de ce tableau stockent l'adresse de chaque fonction virtuelle contenue dans cette classe.
Prenons l'exemple ci-dessous :

vérifier la version Java sous Linux
C++
// C++ program to illustrate> // working of Virtual Functions> #include> using> namespace> std;> class> base {> public>:> >void> fun_1() { cout <<>'base-1
'>; }> >virtual> void> fun_2() { cout <<>'base-2
'>; }> >virtual> void> fun_3() { cout <<>'base-3
'>; }> >virtual> void> fun_4() { cout <<>'base-4
'>; }> };> class> derived :>public> base {> public>:> >void> fun_1() { cout <<>'derived-1
'>; }> >void> fun_2() { cout <<>'derived-2
'>; }> >void> fun_4(>int> x) { cout <<>'derived-4
'>; }> };> int> main()> {> >base* p;> >derived obj1;> >p = &obj1;> >// Early binding because fun1() is non-virtual> >// in base> >p->fun_1();> >// Late binding (RTP)> >p->fun_2();> >// Late binding (RTP)> >p->fun_3();> >// Late binding (RTP)> >p->fun_4();> >// Early binding but this function call is> >// illegal (produces error) because pointer> >// is of base type and function is of> >// derived class> >// p->fun_4(5);> >return> 0;> }> |
b+ arbres
>
>Sortir
base-1 derived-2 base-3 base-4>
Explication: Dans un premier temps, nous créons un pointeur de type classe de base et l'initialisons avec l'adresse de l'objet de classe dérivée. Lorsque nous créons un objet de la classe dérivée, le compilateur crée un pointeur en tant que donnée membre de la classe contenant l'adresse de VTABLE de la classe dérivée.
Un concept similaire de Reliure tardive et précoce est utilisé comme dans l’exemple ci-dessus. Pour l'appel de fonction fun_1(), la version de classe de base de la fonction est appelée, fun_2() est remplacée dans la classe dérivée donc la version de la classe dérivée est appelée, fun_3() n'est pas remplacée dans la classe dérivée et est une fonction virtuelle donc la version de la classe de base est appelée, de même fun_4() n'est pas remplacée donc la version de la classe de base est appelée.
Note: fun_4(int) dans la classe dérivée est différent de la fonction virtuelle fun_4() dans la classe de base car les prototypes des deux fonctions sont différents.
Limites des fonctions virtuelles
- Plus lent : l'appel de fonction prend légèrement plus de temps en raison du mécanisme virtuel et rend l'optimisation plus difficile pour le compilateur car il ne sait pas exactement quelle fonction va être appelée au moment de la compilation. Difficile à déboguer : dans un système complexe, les fonctions virtuelles peuvent rendre un peu plus difficile la détermination d'où une fonction est appelée.