IdentifiantMot de passe
Loading...
Mot de passe oubli� ?Je m'inscris ! (gratuit)
logo

FAQ C++Consultez toutes les FAQ

Nombre d'auteurs : 34, nombre de questions : 368, derni�re mise � jour : 14 novembre 2021  Ajouter une question

 

Cette FAQ a �t� r�alis�e � partir des questions fr�quemment pos�es sur les forums de http://www.developpez.com et de l'exp�rience personnelle des auteurs.

Je tiens � souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose sont correctes ; les auteurs font le maximum, mais l'erreur est humaine. Cette FAQ ne pr�tend pas non plus �tre compl�te. Si vous trouvez une erreur ou si vous souhaitez devenir r�dacteur, lisez ceci.

Sur ce, nous vous souhaitons une bonne lecture.

SommaireLes classes en C++Les fonctions membres virtuelles (6)
pr�c�dent sommaire suivant
 

Le mot-cl� virtual permet de supplanter une fonction membre d'une classe parent depuis une classe d�riv�e, � condition qu'elle ait la m�me signature (� l'exception du retour covariant).

Code c++ : S�lectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class A 
{ 
public: 
    void F1() { cout << "A::F1()\n"; }     
    virtual void F2() { cout << "A::F2()\n"; } 
}; 
  
class B : public A 
{ 
public: 
    void F1() { cout << "B::F1()\n"; }     
    void F2() { cout << "B::F2()\n"; } 
}; 
  
int main() 
{ 
    A a; 
    a.F1(); // affiche "A::F1()" 
    a.F2(); // affiche "A::F2()" 
  
    B b; 
    b.F1(); // affiche "B::F1()" 
    b.F2(); // affiche "B::F2()" 
  
    // copie non polymorphe 
    a = b; 
    a.F1(); // affiche "A::F1()" 
    a.F2(); // affiche "A::F2()" 
  
    // utilisation polymorphe de B (par pointeur) 
    A * pa = &b; 
    pa->F1(); // affiche "A::F1()" 
    pa->F2(); // affiche "B::F2()" <-- gr�ce � virtual 
  
    // utilisation polymorphe de B (par r�f�rence) 
    A & ra = b; 
    ra.F1(); // affiche "A::F1()" 
    ra.F2(); // affiche "B::F2()" <-- gr�ce � virtual 
}
Dans cet exemple, F1() est red�finie statiquement par B, c'est-�-dire que le compilateur utilise le type officiel de la variable pour savoir quelle fonction appeler. Ainsi, si on appelle F1() depuis un objet de type A, ce sera toujours A::F1() qui sera appel�e, et si l'objet est de type B ce sera B::F1() ind�pendamment du fait qu'il peut s'agir d'un pointeur ou d'une r�f�rence sur A qui d�signe en r�alit� un objet de type B (cas d'utilisation polymorphe de B).
L'appel � une fonction membre virtuel n'est au contraire pas d�termin� � la compilation, mais lors de l'ex�cution. Le fait que A::F2() soit d�clar�e virtual et supplant�e par B::F2() signifie qu'� chaque appel de F2(), le compilateur va tester le type r�el de l'objet afin d'appeler B::F2() si possible. Sinon, il appellera A::F2(). On parle alors de liaison dynamique (dynamic binding en anglais) par opposition � la liaison statique faite lors de l'�dition de liens.
La virtualit� implique l'utilisation de pointeurs ou de r�f�rences. Ceci est illustr� par le 3e exemple du code ci-dessus qui effectue une recopie non polymorphe d'un objet B vers un objet A. Dans ce cas, l'objet B est � tronqu� � (pour �viter ce probl�me, il faut passer par une copie polymorphe, voir Comment effectuer la copie d'objets polymorphes ?) et on obtient un objet de type A malgr� que l'on soit parti d'un objet de type B.
Ce n'est pas le cas avec l'utilisation de pointeurs ou r�f�rences, qui bien que d�clar�s comme �tant des pointeurs / r�f�rences sur des objets de types A peuvent d�signer des objets de type B comme dans les deux derniers exemples du code pr�c�dent.
Le type statique de pa est A* mais son type dynamique est B* . De m�me, le type dynamique de ra est B, ce qui explique que pa->F2() et ra.F2() provoquent l'appel de B::F2() alors que statiquement c'est A::F2() qui aurait du �tre appel�.
Notez que cet exemple n'inclut pas de destructeur virtuel par souci de simplification, mais ceci serait n�cessaire. Pour plus d'explications, lire la question Pourquoi et quand faut-il cr�er un destructeur virtuel ?.

Attention : en l'absence de l'introduction du mot virtual, nous aurions affaire � un masquage de nom, et non � une red�finition. Il y aurait alors absence totale de comportement polymorphique.

Mis � jour le 6 juillet 2014 Anomaly Aurelien.Regat-Barrel Luc Hermitte

L'appel dynamique permet d'augmenter la r�utilisabilit� en autorisant le ��vieux�� code � appeler du nouveau code.

Avant l'apparition de l'orientation objet, la r�utilisation du code se faisait en appelant du vieux code � partir du nouveau code. Par exemple, un programmeur peut �crire du code appelant du code r�utilisable comme printf().

Avec l'orientation objet, la r�utilisation peut aussi �tre accomplie via l'appel de nouveau code par de l'ancien. Par exemple, un programmeur peut �crire du code qui est appel� par un framework qui a �t� �crit par son arri�re grand-p�re. Il n'y a pas besoin de modifier le code �crit par l'arri�re grand-p�re. En fait, il n'a m�me pas besoin d'�tre recompil�. Et si jamais il ne restait que le fichier objet, et que le code �crit par l'arri�re grand-p�re ait �t� perdu depuis 25 ans, cet ancien fichier objet appellera le code avec les nouvelles fonctionnalit�s sans rien changer d'autre.

C'est cela l'extensibilit�, et c'est cela l'orientation objet.

Mis � jour le 10 f�vrier 2004 Cline

OUI

Sans les fonctions virtuelles, le C++ ne serait pas un langage orient� objet. La surcharge d'op�rateur et les fonctions membres non virtuelles sont tr�s pratiques, mais ne sont, finalement qu'une variante syntaxique de la notion beaucoup plus classique de passage de pointeur sur une structure � une fonction. La biblioth�que standard contient de nombreux templates illustrant les techniques de � programmation g�n�rique �, qui sont aussi tr�s pratiques, mais les fonctions virtuelles sont le cour m�me de la programmation orient�e objet.

D'un point de vue 'business', il y a tr�s peu de raison de passer du C pur au C++ sans les fonctions virtuelles (pour le moment, nous ferons abstraction de la programmation g�n�rique et de la biblioth�que standard). Les sp�cialistes pensent souvent qu'il a une grande diff�rence entre le C et le C++ non orient� objet ; mais sans l'orientation objet, la diff�rence n'est pas suffisante pour justifier le co�t de formation des d�veloppeurs, des nouveaux outils.
En d'autres termes, si je devais conseiller un gestionnaire concernant le passage du C au C++ sans orientation objet (c'est-�-dire changer le langage sans changer de paradigme), je le d�couragerais probablement, � moins qu'il y ait des contraintes li�es aux outils utilis�s. D'un point de vue gestion, la programmation orient�e objet permet de concevoir des syst�mes extensibles et adaptables, mais la syntaxe seule sans l'orientation objet ne r�duira jamais le co�t de maintenance, mais augmentera probablement les co�ts de formation de fa�on significative.

Nota : le C++ sans virtualit� n'est pas orient� objet. Programmer avec des classes mais sans liaison dynamique est une programmation bas�e sur des objets, mais n'est pas de la programmation objet. Ignorer la virtualit� est �quivalent � ignorer l'orientation objet. Tout ce qui reste est une programmation bas�e sur des objets, tout comme la version originale d'ADA. (Soit dit en passant, le nouvel ADA supporte la v�ritable orientation objet, et non plus simplement la programmation bas�e sur les objets).

Mis � jour le 10 f�vrier 2004 Cline

Syntaxiquement, une fonction virtuelle pure est une fonction virtuelle suivie de � = 0 � dans sa d�claration :

Code c++ : S�lectionner tout
1
2
3
4
5
class Test 
{ 
public: 
    virtual void F() = 0;  // = 0 signifie "virtuelle pure" 
};
Une fonction virtuelle signifie qu'elle peut �tre supplant�e par une fonction d'une classe fille.
Une fonction virtuelle pure signifie qu'elle doit �tre supplant�e par une fonction d'une classe fille.
La classe qui d�clare une fonction virtuelle pure n'est alors pas instanciable car elle poss�de au moins une fonction qui doit �tre supplant�e. On dit alors que c'est une classe abstraite (lire Qu'est-ce qu'une classe abstraite ?).
Notez que virtuelle pure veut simplement dire que la fonction doit �tre supplant�e par les classes filles que l'on veut instanciables, et non que la fonction n'est pas impl�ment�e. Le C++ autorise une fonction virtuelle pure � disposer d'un corps. Une telle pratique sert g�n�ralement � forcer une classe � �tre abstraite (en rendant son destructeur virtuel pur) ou � proposer une impl�mentation type pour la fonction virtuelle pure.

Code c++ : S�lectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A 
{ 
public: 
    virtual void f() = 0; // virtuelle pure 
}; 
  
void A::f() 
{ 
    // impl�mentation par d�faut 
} 
  
// B se contente de l'impl�mentation par d�faut de f() 
class B : public A 
{ 
public: 
    void f() 
    { 
        A::f(); 
    } 
};

Mis � jour le 22 novembre 2004 Aurelien.Regat-Barrel LFE Luc Hermitte

Lors de la r�impl�mentation d'une fonction membre virtuelle dans une classe d�riv�e, il est possible de ne pas tout � fait respecter le prototype de la fonction virtuelle en renvoyant un type d�riv� du type originel :

Code c++ : S�lectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class A {}; 
class B : public A {}; 
  
class Base 
{ 
public: 
    virtual A* Test() 
    { 
        cout << "Base::Test\n"; 
        return new A; 
    } 
}; 
  
class Derived : public Base 
{ 
public: 
    virtual B* Test() // le type de retour est diff�rent de Base::Test 
    { 
        cout << "Derived::Test\n"; 
        return new B; 
    } 
}; 
  
int main() 
{    
    Base *b = new Derived; 
    A *a = b->Test(); 
}
Si votre compilateur supporte les retours covariants, alors le message � Derived::Test � devrait s'afficher, et a devrait pointer vers une instance de B. Si � Base::Test � s'affiche, c'est que le compilateur a consid�r� que le prototype de Derived::Test ne correspondait pas � celui de Base::Test et qu'il s'agissait donc d'une nouvelle fonction membre propre � Derived et non pas d'une r�impl�mentation de Base::Test (voir Qu'est-ce que le masquage de fonction ?). Dans ce cas, ou en cas de refus de compilation, votre compilateur ne supporte pas les retours covariants (cas de VC++ 6).

Cette possibilit� est en particulier utilis�e dans le clonage de classes polymorphes. Les retours covariants permettent en effet de transformer le code suivant :

Code c++ : S�lectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Clonable 
{ 
public: 
    virtual Clonable* Clone() const = 0; 
}; 
  
class A : public Clonable 
{ 
public: 
    virtual Clonable* Clone() const 
    { 
        return new A( *this ); 
    } 
}; 
  
int main() 
{ 
    A *a1 = new A; 
    // faire une copie 
    A *a2 = static_cast<A*>( a1->Clone() ); // cast obligatoire ! 
}
En une version plus �l�gante sans cast :

Code c++ : S�lectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Clonable 
{ 
public: 
    virtual Clonable* Clone() const = 0; 
}; 
  
class A : public Clonable 
{ 
public: 
    virtual A* Clone() const // retour covariant 
    { 
        return new A( *this ); 
    } 
}; 
  
int main() 
{ 
    A *a1 = new A; 
    // faire une copie 
    A *a2 = a1->Clone(); // plus de cast 
}
Pour plus de d�tails � ce sujet, lire Comment effectuer la copie d'objets polymorphes ?.

Mis � jour le 17 octobre 2005 Aurelien.Regat-Barrel

Oui, c'est possible, mais attention, �a ne fait pas toujours ce qu'on pense. La premi�re approche consiste � comprendre que lors de l'appel du constructeur d'une classe de base, la classe d�riv�e n'a pas encore �t� construite. Donc, c'est la m�thode sp�cialis�e � ce niveau qui est appel�e.
Voyons en d�tail :
La r�gle est que le type dynamique d'une variable en cours de construction est celui du constructeur qui est en train d'�tre ex�cut�. Pour bien comprendre ce qui se passe, il faut donc revenir sur la diff�rence entre le type statique d'une variable, et son type dynamique.

Prenons par exemple trois classes, C qui d�rive de B qui d�rive de A. Par exemple, dans :

Code c++ : S�lectionner tout
A* a = new B();
La variable a poss�de comme type statique (son type d�clar� dans le programme) A*.
Par contre, son type dynamique est B*. Une fonction virtuelle est simplement une fonction dont on va chercher le code en utilisant le type dynamique de la variable, au lieu de son type statique, comme une fonction classique.

Maintenant, quand on cr�e un objet de type C, les choses se passent ainsi :
  • On alloue assez de m�moire pour un objet de la taille de C.
  • On initialise la sous partie correspondant � A de l'objet.
  • On appelle le corps du constructeur de A. Pendant cet appel, l'objet cr�e a pour type dynamique A.
  • On initialise la sous partie correspondant � B de l'objet.
  • On appelle le corps du constructeur de B. Pendant cet appel, l'objet cr�e a pour type dynamique B.
  • On initialise la sous partie correspondant � C de l'objet.
  • On appelle le corps du constructeur de C. Pendant cet appel, l'objet cr�e a pour type dynamique C.

Donc, dans le corps du constructeur de la classe B, un appel d'une fonction virtuelle appellera la version de la fonction d�finie dans la classe B (ou � d�faut celle d�finie dans A si la fonction n'a pas �t� d�finie dans B), et non pas celle d�finie dans la classe C.

D'ailleurs, si la fonction est virtuelle pure dans B, �a causera quelques probl�mes, puisqu'on tentera alors d'appeler une fonction qui n'existe pas. En g�n�ral, le programme va planter, si on a de la chance, il affichera une message du style � Pure function called �.

La probl�matique est exactement la m�me pour les destructeurs, mais dans l'ordre inverse.

Pourquoi cette r�gle ? Une fonction d�finie dans C a acc�s aux donn�es membre de C. Or, on a vu que au moment o� on ex�cute l'appel au corps du constructeur de B, ces derni�res ne sont pas encore cr��es. On a donc pr�f�r� jouer la s�curit�.

Mis � jour le 15 octobre 2009 JolyLoic Laurent Gomila

Proposer une nouvelle r�ponse sur la FAQ

Ce n'est pas l'endroit pour poser des questions, allez plut�t sur le forum de la rubrique pour �a


R�ponse � la question

Liens sous la question
pr�c�dent sommaire suivant
 

Les sources pr�sent�es sur cette page sont libres de droits et vous pouvez les utiliser � votre convenance. Par contre, la page de pr�sentation constitue une �uvre intellectuelle prot�g�e par les droits d'auteur. Copyright � 2025 Developpez Developpez LLC. Tous droits r�serv�s Developpez LLC. Aucune reproduction, m�me partielle, ne peut �tre faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'� trois ans de prison et jusqu'� 300 000 � de dommages et int�r�ts.