-
Les fonctions virtuelles en C++ :Types statiques et types
dynamiques
Par 3DArchi
Date de publication : 19 novembre 2009
Les fonctions virtuelles sont un des piliers de la programmation
oriente objet. En favorisantl'abstraction, elles permettent la
construction d'architectures logicielles stables et volutives.Cet
article se propose d'explorer les fonctions virtuelles dans le
langage C++ en abordantaussi bien les problmes syntaxiques que les
consquences smantiques de leur utilisation.Vous pouvez lire cet
article sur une seule page.Vous pouvez lire cet article sur une
version multi-page.Commentaires, conseils et ractions dans cette
discussion :
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 2 -Copyright 2009 3DArchi. Aucune reproduction, mme partielle,
ne peut tre faite de ce site et de l'ensemble de son contenu :
textes, documents, images, etc. sans l'autorisation expresse
del'auteur. Sinon vous encourez selon la loi jusqu' trois ans de
prison et jusqu' 300 000 de dommages et intrts. Cette page est
dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
I - Les fonctions membres en
C++.............................................................................................................................
4I-A - Les fonctions membres
statiques..................................................................................................................
4I-B - Les fonctions
normales..................................................................................................................................5I-C
- Les fonctions
virtuelles..................................................................................................................................
6I-D - La surcharge de
fonction...............................................................................................................................8I-E
- Quand l'hritage chamboule tout
!...............................................................................................................10
II - Type statique et type
dynamique.........................................................................................................................12III
- Types et appel de
fonction.................................................................................................................................
15IV - A quoi servent les fonctions virtuelles
?.............................................................................................................19V
- Premire consquence : comment bien dclarer son
destructeur......................................................................20VI
- Seconde consquence : inlining de fonctions et fonctions
virtuelles.................................................................
24VII - Quand est construit le type dynamique ou quel est l'impact
des appels de fonctions virtuelles dans unconstructeur
?.............................................................................................................................................................26VIII
- Que devient le type dynamique lors de la destruction de l'objet
ou peut-on appeler des fonctions virtuellesdans un destructeur
?................................................................................................................................................
28IX - Construction, destruction, fonctions virtuelles et
multithreading........................................................................
29X - Une vision plus avance des appels de fonctions virtuelles
dans les constructeurs et destructeurs.................. 29XI - Et
pour les fonctions virtuelles pures
?..............................................................................................................
31
XI-A - Fonctions virtuelles pures, classes abstraites, classes
concrtes............................................................
31XI-B - Appel d'une fonction virtuelle
pure............................................................................................................
34XI-C - Un destructeur virtuel
pur..........................................................................................................................38XI-D
- Appel d'une fonction virtuelle pure dans le
constructeur/destructeur d'une classe
abstraite.....................39XI-E - Fonctions virtuelles pures
et constructeur/destructeur des classes
concrtes..........................................42
XII - L'operateur d'affectation :
operator=..................................................................................................................43XIII
- Le retour covariant des fonctions
virtuelles......................................................................................................45XIV
- Forcer un appel spcifique d'une fonction
virtuelle.........................................................................................
49XV - Fonctions virtuelles et
visibilit..........................................................................................................................50XVI
- Fonction virtuelle et masquage de
fonction.....................................................................................................
53
XVI-A - Masquage d'une fonction virtuelle par une fonction non
virtuelle...........................................................
53XVI-B - Masquage d'une fonction non virtuelle par une fonction
virtuelle...........................................................
60XVI-C - Des fonctions pas totalement
masque.................................................................................................
61XVI-D - Ramener un symbole :
using..................................................................................................................62XVI-E
- Que conclure
?........................................................................................................................................
62
XVII - Fonctions virtuelles et fonctions gnriques
(template)..................................................................................63XVII-A
- Fonctions
template.................................................................................................................................
63XVII-B - Fonctions virtuelles dans des classes
gnriques.................................................................................63
XVIII - Fonctions virtuelles et amiti
(friend).............................................................................................................
66XIX - Fonctions virtuelles et spcification
d'exceptions.............................................................................................68
XIX-A - Rappel sur les
exceptions.......................................................................................................................68XIX-B
- Exceptions et hirarchie de
classes.......................................................................................................
69XIX-C - Les exceptions d'une fonction
virtuelle...................................................................................................
70
XX - Fonctions virtuelles et programmation par
contrat............................................................................................71XX-A
- Un rapide
rappel......................................................................................................................................
71XX-B - Le principe de substitution de
Liskov.......................................................................................................72XX-C
- Impact sur les invariants pour une fonction
virtuelle...............................................................................
72XX-D - Impact sur les prconditions pour une fonction
virtuelle.........................................................................
73XX-E - Impact sur les postconditions pour une fonction
virtuelle........................................................................
74
XXI - Le pattern
N.V.I................................................................................................................................................
75XXII - Informations sur les types (dynamiques et statiques) et
conversions............................................................
76
XXII-A - Types
polymorphes................................................................................................................................
76XXII-B - Comment connatre le type dynamique d'une variable
?.......................................................................76
XXII-B-1 - L'oprateur
typeid..........................................................................................................................
76XXII-B-2 - Evaluation de
l'expression.............................................................................................................
78XXII-B-3 - La classe
type_info........................................................................................................................80XXII-B-4
- Pourquoi rcuprer le type dynamique
?......................................................................................
81
XXII-C - Comment connatre le type statique d'une variable
?...........................................................................
81XXII-D - Conversions entre type de base et type
driv.....................................................................................83
XXII-D-1 - Conversion du type driv vers le type de
base...........................................................................83
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 3 -Copyright 2009 3DArchi. Aucune reproduction, mme partielle,
ne peut tre faite de ce site et de l'ensemble de son contenu :
textes, documents, images, etc. sans l'autorisation expresse
del'auteur. Sinon vous encourez selon la loi jusqu' trois ans de
prison et jusqu' 300 000 de dommages et intrts. Cette page est
dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
XXII-D-2 - Conversion du type de base vers le type
driv...........................................................................83XXII-D-3
- Pourquoi faire une conversion d'un type de base vers un type
driv..........................................89
XXIII - Comment a marche
?...................................................................................................................................89XXIII-A
- Qui dfinit la mise en oeuvre
?.............................................................................................................89XXIII-B
- Rsoudre l'appel dynamiquement : les tables
virtuelles.......................................................................
89XXIII-C - Quelle entre pour les fonctions virtuelles pures
?...............................................................................92XXIII-D
- Comment sont construites les tables virtuelles
?.................................................................................
92XXIII-E - Comment sont dtruites les tables virtuelles
?.....................................................................................
94XXIII-F - Qu'est-ce qu'un pointeur de fonction virtuelle
?....................................................................................
94
XXIV - A
retenir.........................................................................................................................................................
95XXV - Un peu de
lecture...........................................................................................................................................96XXVI
-
Remerciements..............................................................................................................................................
97
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 4 -Copyright 2009 3DArchi. Aucune reproduction, mme partielle,
ne peut tre faite de ce site et de l'ensemble de son contenu :
textes, documents, images, etc. sans l'autorisation expresse
del'auteur. Sinon vous encourez selon la loi jusqu' trois ans de
prison et jusqu' 300 000 de dommages et intrts. Cette page est
dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
I - Les fonctions membres en C++
Trois types de fonctions peuvent tre dfinis dans une classe (1)
en C++ :
les fonctions membres statiques ; les fonctions membres normales
; les fonctions membres virtuelles.
I-A - Les fonctions membres statiques
Le mot-cl static utilis en dbut du prototype de la fonction
permet de dclarer une fonction membre statique ouencore fonction de
classe :
Exemple de fonction membre statique :struct my_type{ static void
s_function(); // fonction statique};
Une fonction membre statique n'est pas lie un objet. Elle n'a
donc pas de paramtre implicite this et ne peut doncaccder d'autres
membres d'instances de la classe sinon les membres statiques :
Appel d'une fonction membre statique :#include struct my_type{
static void s_function(); // fonction membre statique
static int mi_class_variable; int mi_member_variable;};int
my_type::mi_class_variable=0;void my_type::s_function(){
std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 5 -Copyright 2009 3DArchi. Aucune reproduction, mme partielle,
ne peut tre faite de ce site et de l'ensemble de son contenu :
textes, documents, images, etc. sans l'autorisation expresse
del'auteur. Sinon vous encourez selon la loi jusqu' trois ans de
prison et jusqu' 300 000 de dommages et intrts. Cette page est
dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
L'appel d'une fonction membre statique d'une classe ne dpend pas
d'une instance dece type.
Par consquent, les fonctions membres statiques ne nous
intresseront pas par la suite car elles ne dpendentnullement d'un
objet.
I-B - Les fonctions normales
Les fonctions normales n'ont pas de mot-cl spcifique pour leur
dclaration : c'est le comportement par dfaut d'unefonction membre
d'une classe.
Exemple de fonction membre normale :#include struct my_type{
void a_function(); // fonction membre normale};void
my_type::a_function(){ std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 6 -Copyright 2009 3DArchi. Aucune reproduction, mme partielle,
ne peut tre faite de ce site et de l'ensemble de son contenu :
textes, documents, images, etc. sans l'autorisation expresse
del'auteur. Sinon vous encourez selon la loi jusqu' trois ans de
prison et jusqu' 300 000 de dommages et intrts. Cette page est
dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
I-C - Les fonctions virtuelles
Les fonctions virtuelles prcdent leur dclaration du mot cl
virtual (2) :
Exemple d'une fonction virtuelle :#include struct my_type{
virtual void a_function(); // fonction virtuelle};void
my_type::a_function() // dans la dfinition de la fonction, il ne
faut // pas rpter le mot-cl 'virtual'{ std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 7 -Copyright 2009 3DArchi. Aucune reproduction, mme partielle,
ne peut tre faite de ce site et de l'ensemble de son contenu :
textes, documents, images, etc. sans l'autorisation expresse
del'auteur. Sinon vous encourez selon la loi jusqu' trois ans de
prison et jusqu' 300 000 de dommages et intrts. Cette page est
dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
En C++, les fonctions virtuelles doivent tre membres d'une
classe.
Anticipons en signalant l'existence d'une catgorie particulire
de fonctions virtuelles en C++ : les fonctions virtuellespures. Une
fonction virtuelle pure est une fonction virtuelle laquelle est
rajoute =0 la fin de sa dclaration :
Exemple d'une fonction virtuelle pure :struct my_type{ virtual
void a_function()=0; // fonction virtuelle pure};
Nous reviendrons un peu plus loin sur les fonctions virtuelles
pures, pour l'instant il suffit de savoir que a existe etqu'avant
d'tre "pures", ce sont avant tout des fonctions virtuelles.
Toutes les fonctions d'une classe peuvent-elles tre virtuelles ?
Oui, efin presque : seul les constructeurs (et lesfonctions
statiques) ne peuvent pas tre virtuels. Toutes les autres fonctions
le peuvent : que ce soit le destructeur, lesoprateurs, ou des
fonctions quelconques. Nous verrons par la suite ce que cela
signifie et l'intrt dans chaque cas.
Toutes (ou presque) les fonctions peuvent tre virtuellesstruct
my_type{// virtual my_type(); // erreur : un constructeur ne peut
tre virtuel// virtual static void s_function(); // erreur : une
fonction ne peut tre ET statique ET virtuelle virtual ~my_type();//
OK virtual void function(); // OK virtual my_type&
operator+(my_type const&); // OK};
La virtualit s'hrite : une fonction virtuelle dans la classe de
base reste virtuelle dans la classe drive mme si lemot cl virtual
n'est pas accol :
La virtualit s'hrite :struct base{ void function_1(); virtual
void function_2(); void function_3();};
struct derived : public base{ void function_1(); void
function_2(); virtual void function_3();};
Nous avons avec cet exemple :
base derivedfunction_1 non virtuelle non virtuellefunction_2
virtuelle virtuellefunction_3 non virtuelle virtuelle
Autant comme le montre la troisime ligne, il est possible de
masquer une fonction non virtuelle d'une classe de basepar une
fonction virtuelle dans une classe drive, autant il est impossible
de s'en dbarrasser. Une fonction est etsera virtuelle pour toutes
les classes drivant de la classe l'ayant dfinie comme telle.
Cependant, afin d'viter touteconfusion, il est fortement recommand
d'utiliser le mot-cl virtual dans les classes drives :
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 8 -Copyright 2009 3DArchi. Aucune reproduction, mme partielle,
ne peut tre faite de ce site et de l'ensemble de son contenu :
textes, documents, images, etc. sans l'autorisation expresse
del'auteur. Sinon vous encourez selon la loi jusqu' trois ans de
prison et jusqu' 300 000 de dommages et intrts. Cette page est
dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Les classes drives devraient utiliser le mot-cl virtual pour les
fonctions dfinies commevirtuelles dans la classe de base.
I-D - La surcharge de fonction
Introduisons un dernier point pour poser notre problmatique : la
surcharge de fonction. Il est aussi possible desurcharger une
fonction dans une classe, c'est dire dfinir plusieurs fonctions
avec le mme nom, conditionqu'elles diffrent par :
leur nombre d'arguments ; et/ou le type d'au moins un des
arguments ; et/ou leur constance.
La signature d'une fonction en C++ dsigne son nom, le nombre de
ses arguments, leur type et la constance de lafonction. Surcharger
une fonction F1 revient alors proposer une nouvelle fonction F2,
telle que la signature de F1est diffrente de celle de F2 autrement
que par le nom qu'elles partagent.Par exemple, le code suivant
prsente diffrentes surcharges d'une fonction membre :
Surcharges d'une fonction membre :struct my_type{ void
function(double,double){} void function(double){} // le nombre
d'argument est diffrent // de la prcdente dfinition void
function(int){} // le type de l'argument est diffrent // de la
prcdente dfinition void function(int) const {} // la constance est
diffrente // de la prcdente dfinition void function(char&){}
void function(char const &){} // char& et char const &
sont deux types diffrents};
La surcharge est un cas de polymorphisme en C++ (3) . Cela
permet d'adapter la fonction appeler selon lesarguments en paramtre
:
Appels des diffrentes surcharges d'une fonction membre :#include
struct my_type{ void function(double,double) // (1) { std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 9 -Copyright 2009 3DArchi. Aucune reproduction, mme partielle,
ne peut tre faite de ce site et de l'ensemble de son contenu :
textes, documents, images, etc. sans l'autorisation expresse
del'auteur. Sinon vous encourez selon la loi jusqu' trois ans de
prison et jusqu' 300 000 de dommages et intrts. Cette page est
dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Appels des diffrentes surcharges d'une fonction membre : {
std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 10 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Surcharges interdites : typedef int t_int; void
function_5(t_int); // Erreur : le typedef n'est qu'un synonyme
void function_6(char); void function_6(char const); // Erreur :
le const n'est pas significatif // pour diffrencier les deux
fonctions
void function_6_bis(char *); void function_6_bis(char * const);
// Erreur : le const n'est pas significatif // pour diffrencier les
deux fonctions void function_6_ter(char *); void
function_6_ter(char const *); // OK : char const * et char * sont
bien // deux types diffrents void function_7(int ); void
function_7(int =42); // Erreur : les dfinitions sont
quivalentes};
I-E - Quand l'hritage chamboule tout !
L'hritage importe dans la classe drive toutes les dclarations
des classes de bases (4) :
Hritage des membres de la classe parent :struct base{ void
function(){}};
struct derived : base{};
int main(){ derived d; d.function(); // on rcupre l'interface de
base
return 0;}
Cependant, une fonction dclare dans une classe drive ayant le
mme nom qu'une fonction de la classe de basemais avec une signature
diffrente masque la fonction de la classe de base dans la classe
drive :
Masquage des fonctions de base dans la classe drive :struct
base{ void function(){}};
struct derived : base{ void function(int){} void
call_a_function() { function(); // Erreur base::function est masque
par derived::function base::function(); // OK : on indique
explicitement la fonction appeler function(1); // appel de
derived::function(int) }};
int main()
(4) Bien sr, la visibilit des fonctions (publique, protge ou
prive) combine celle de l'hritage limite les caso l'appel est
valide. On lira avec profit l'entre de la F.A.Q. suivante : Que
signifient public, private etprotected ?
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 11 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Masquage des fonctions de base dans la classe drive :{ derived
d; d.function(); // Erreur base::function est masque par
derived::function d.base::function(); // OK : on indique
explicitement la fonction appeler d.function(1); // appel de
derived::function(int)
return 0;}
La surcharge dans une classe drive d'une fonction dfinie dans
une classe de baseavec une signature diffrente masque la fonction
de la classe de base dans et pour laclasse drive.
Nous avons vu la section prcdente qu'il n'tait pas possible dans
une mme classe de redfinir une nouvellefonction avec la mme
signature qu'une fonction existante (mme nom, mme paramtres, mme
constance). Et,nous en arrivons au point qui va nous intresser,
savoir :
Une classe drive peut redfinir une fonction d'une classe de base
ayant la mmesignature !
Ainsi, en reprenant dans la section prcdente l'exemple des
surcharges interdites et en recopiant les surchargesinterdites vers
la classe drive, nous obtenons le code valide suivant :
Tout redevient possible avec l'hritage :struct base{
void function_1();
static void function_2();
virtual void function_3();
virtual void function_4();
void function_5(int);
void function_6(char); void function_7(int );};
struct derived : public base{ int function_1(); // OK void
function_2(); // OK void function_2()const; // OK void
function_3(); // OK virtual void function_4()=0; // OK typedef int
t_int; // OK void function_5(t_int); // OK void function_6(char
const); // OK void function_7(int =42); // OK};
L'objectif est maintenant de savoir quelles fonctions sont
appeles lorsqu'une mme signature est disponible dansune classe
drive et une classe de base selon l'expression utilise pour l'appel
:
Quelle est la fonction appele ?struct base{ void function_1() {
}
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 12 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Quelle est la fonction appele ? virtual void function_2() { }
void call_function_1() { function_1(); // Quelle est la fonction
appele ? } void call_function_2() { function_2(); // Quelle est la
fonction appele ? }};
struct derived : public base{ void function_1() { } virtual void
function_2() { } void call_function_1() { function_1(); // Quelle
est la fonction appele ? } void call_function_2() { function_2();
// Quelle est la fonction appele ? }};
int main(){ base b; b.function_1(); // Quelle est la fonction
appele ? b.function_2(); // Quelle est la fonction appele ?
derived d; d.function_1(); // Quelle est la fonction appele ?
d.function_2(); // Quelle est la fonction appele ?
base &rb = b; rb.function_1(); // Quelle est la fonction
appele ? rb.function_2(); // Quelle est la fonction appele ?
base &rd = d; rd.function_1(); // Quelle est la fonction
appele ? rd.function_2(); // Quelle est la fonction appele ?
return 0;}
Pour arriver rpondre toutes ces questions, il nous faut
introduire une nouvelle notion : le type statique et le
typedynamique d'une variable.
II - Type statique et type dynamique
Une variable possde deux types : un type statique et un type
dynamique.Le type statique d'une variable est celui dtermin la
compilation. Le type statique est le plus vident : c'est celuiavec
lequel vous avez dclar votre variable. Il est sous votre nez
lorsque vous regardez le code.Le type dynamique d'une variable est
celui dtermin l'excution. Le type dynamique quant lui n'est
pasimmdiat en regardant le code. En effet, il va pouvoir varier
l'excution selon ce que la variable va effectivementdsigner pendant
le droulement du programme.Le type dynamique et le type statique
concident pour les variables utilises par valeur :
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 13 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Type statique et type dynamique de variables par valeur :int
a;char c;std::string s;class a_class{/*[...]*/};a_class
an_object;enum E_enumeration{/*[...]*/};E_enumeration e;
Avec cet exemple, on a :
variable type statique type dynamiquea int intc char chars
std::string std::stringan_object a_class a_classe E_enumeration
E_enumeration
Encore une fois, l'hritage introduit une diffrence entre un type
dynamique et un type statique. Cette diffrenceapparat avec les
pointeurs et les rfrences quand le type dclar du pointeur ou de la
rfrence n'est pas le typede l'objet effectivement point (resp.
rfrenc) l'excution. Le type statique est celui dfini dans le code,
le typedynamique d'une rfrence ou d'un pointeur est celui de
l'objet rfrenc (resp. point) :
Type statique, type dynamique, hritage, pointeurs et rfrences
:struct base {};struct derived : public base {};
int main(){
base b; derived d; base &rb = b; base &rd = d; base *pb
= &b; base *pd = &d; return 0;}
Avec l'exemple, ci-dessus, les types des variables (5) sont
:
variable type statique type dynamiquebase b base basederived d
derived derivedbase &rb = b base basebase &rd = d base
derivedbase *pb = &b base basebase *pd = &d base
derived
Seul les pointeurs et les rfrences vers des instances de classe
ou de structure ont destypes dynamiques et des types statiques
pouvant diverger.
(5) Par facilit d'criture, dans ce document, pour une variable
Type *p_var, on dira que son type est Type alors queformellement
p_var est de type Type* (pointeur de Type) et c'est *p_var qui est
de type Type . De mme, pourles rfrences Type & r_var, on dira
que le type de r_var est Type et non Type&. Enfin, de la mme
faon on ferapudiquement l'impasse sur les qualifications const
et/ou volatile qui peuvent prciser le type rel d'une variable.
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 14 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Le type statique d'un objet dans une fonction membre est celui o
se droule la fonction. Le type dynamique, thistant un pointeur,
dpend de l'objet effectif sur lequel s'applique la fonction. Type
statique et type dynamique peuventalors tre diffrents :
Type de this :class base{public: void function() { // Quel est
le type static de this ? // Quel est le type dynamique de this ?
}};class derived : public base{public: void function_2() { // Quel
est le type static de this ? // Quel est le type dynamique de this
? }};int main(){ base b; b.function(); derived d; d.function();
d.function_2();
return 0;}
fonction type statique de this type dynamiquede this
Pour l'appelb.fonction,dans la fonctionbase::fonction
base base
Pour l'appelde d.fonction,dans la fonctionbase::fonction
base derived
Pour l'appel ded.fonction_2dans la
fonctionderived::fonction_2
derived derived
Le type statique d'une variable est soit le type dynamique de
cette variable soit une classede base directe ou indirecte du type
dynamique.
Toute autre combinaison est une erreur pouvant aboutir un
plantage ou un comportement indtermin.
Attention, si nous avons dit que le pointeur a un type statique
diffrent de son type dynamique, cela s'applique aussibien au
pointeur non drfrenc qu'au pointeur drfrenc :
Types statiques et dynamiques d'un pointeur :struct base
{};struct derived : public base {};
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 15 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Types statiques et dynamiques d'un pointeur :int main(){
base b; derived d; base *pd = &d; return 0;}
variable type statique type dynamiquepd base* derived**pd base
derived
III - Types et appel de fonction
Lorsqu'une expression contient un appel d'une fonction sur un
objet donn, la fonction effectivement appele dpendde plusieurs
paramtres :
la fonction est-elle virtuelle ou non ? la fonction est-elle
appele sur un objet par valeur ou sur une rfrence/pointeur ?
Selon la rponse, la rsolution de l'appel est faite la
compilation ou l'excution :
fonctionnon virtuelle
fonction virtuelle
appel sur un objet COMPILATION COMPILATIONappel sur unerfrence
ou unpointeur
COMPILATION EXCUTION
La rsolution la compilation utilise le type statique car c'est
le seul connu ce moment.La rsolution l'excution se base sur le type
dynamique.Ce qui donne :
fonctionnon virtuelle
fonction virtuelle
appel sur un objet type statique type statiqueappel sur
unerfrence ou unpointeur
type statique type dynamique
Un peu de code pour illustrer tout cela :
Rsolution la compilation ou l'excution des appels :#include
struct base{ void function_1() { std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 16 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Rsolution la compilation ou l'excution des appels : }
virtual void function_2() { std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 17 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
variable type statique type dynamiquebase b base basederived d
derived derivedbase &rb = b base basebase &rd = d base
derived
fonction_1 est une fonction non virtuelle et fonction_2 est une
fonction virtuelle. Soit en reprenant le tableau prcdentprcisant la
fonction appele :
Appelssur b
fonctionnon
virtuelle(fonction_1)
fonctionvirtuelle
(fonction_2)
Appelssur d
Appelssurrb
Appelssurrd
appelsurunobjet
typestatique:base::fonction_1
typestatique:base::fonction_2
appelsurunobjet
typestatique:derived::fonction_1
typestatique:derived::fonction_2
appelsurunerfrence
typestatique:base::fonction_1
typedynamique:base::fonction_2
appelsurunerfrence
typestatique:base::fonction_1
typedynamique:derived::fonction_2
Cette rsolution dynamique prsente avec des rfrences fonctionne
de la mme faon avec un pointeur qu'il soitutilis en tant que tel ou
drfrenc :
Rfrences et pointeurs : mme combat !#include
struct base{ virtual void function() { std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 18 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Rfrences et pointeurs : mme combat ! pd->function();
(*pd).function(); rd.function();
return 0;}
Le comportement est identique si l'expression utilise un
pointeur de fonction :
Appel de fonction avec un pointeur de fonction membre
:#include
struct base{ virtual ~base(){} virtual void function_1() {
std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 19 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
base::function_2
La liaison tardive prenant appui sur le type dynamique telle que
nous la dcrivons ici est valable presque tout letemps. Comme nous
allons le voir par la suite, ce mcanisme prsente quelque subtilit
en particulier lors des phasessensibles que sont la construction ou
la destruction d'un objet.
IV - A quoi servent les fonctions virtuelles ?
L'utilisation du type dynamique pour rsoudre l'appel d'une
fonction virtuelle est unedes grandes forces de la programmation
oriente objet (POO). Elle permet d'adapteret de faire voluer un
comportement dfini dans une classe de base en spcialisantles
fonctions virtuelles dans les classes drives. La substitution d'un
objet de typedynamique drivant du type statique prsent dans
l'expression contenant l'appel vers unefonction virtuelle s'inscrit
dans le cadre du polymorphisme d'inclusion.
Ce polymorphisme d'inclusion permet l'abstraction dans un
logiciel orient objet. Ainsi, un objet peut tre manipul partir d'un
pointeur ou d'une rfrence vers la classe de base. Les membres
publics de la classe de base dterminentles services proposs et la
mise en oeuvre est dlgue aux classes drives apportant des points de
variations ouspcialisant des comportements dans les fonctions
virtuelles :
Le polymorphisme d'inclusion : un principe fondamental de
l'objet !#include struct shape{ virtual void draw() const {
std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 20 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
fonction draw_a_shape peut dessiner tout type d'objet non prvu
lors de son criture du moment que ces objetssont d'un type
dynamique hritant de shape et spcialisant ses fonctions virtuelles.
Il devient possible de rajouter denouveaux types et de pouvoir les
dessiner sans avoir rcrire la fonction draw_a_shape.
Les fonctions virtuelles rduisent le couplage entre une classe
ou une fonction cliente etune classe fournisseur en dlguant aux
classes drives la ralisation d'une partie desservices proposs par
la classe de base.
Les fonctions virtuelles sont un mcanisme pour la mise en oeuvre
du principe ouvert/ferm (6) en permettant de faire voluer une
application par l'ajout de nouvelle classedrive (ouvert) sans avoir
toucher le code existant utilisant l'interface de la classe debase
(ferm).
Les fonctions virtuelles favorisent la rutilisation. Toutes les
fonctions ou les classess'appuyant sur des rfrences ou des
pointeurs de la classe de base peuvent tredirectement utilises avec
des objets d'un nouveau type driv.
Si le terme complet est polymorphisme d'inclusion, beaucoup de
documents en C++ (articles - et celui-ci n'chappepas la rgle - ,
cours, livres, etc.) omettent de prciser d'inclusion. Polymorphe,
polymorphique, polymorphisme,polymorphiquement s'emploient souvent
seuls ds qu'il s'agit de parler d'hritage et donc de la
manipulation d'un objetd'une classe drive partir d'une rfrence ou
d'un pointeur d'une de ses classes de bases avec en arrire plan
lemcanisme des fonctions virtuelles. C'est un raccourci qui peut
faire oublier les autres formes de polymorphisme quine s'appuient
pas sur les fonctions virtuelles et le mcanisme d'hritage. Il faut
juste se souvenir que le polymorphismene se rduit pas au
polymorphisme d'inclusion.
V - Premire consquence : comment bien dclarer son
destructeur
Prenons maintenant l'exemple suivant :
Erreur : un hritage public sans destructeur virtuel
!#include
struct base{ ~base() { std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 21 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Un rsultat parmi d'autres :Destruction de l'objet :destructeur
de base
Le destructeur de la classe derived n'est pas appel ! Conclusion
: le destructeur d'une classe de base pouvant tredtruit de faon
polymorphique DOIT tre virtuel.
En fait, la norme est beaucoup plus svre : l'appel d'un
destructeur non virtuel sur unpointeur dont le type dynamique est
diffrent du type statique provoque un comportementindtermin (7) .
Si les compilateurs se contentent de rsoudre l'appel statiquement
lacompilation, il est fort possible qu'un jour ou l'autre le mme
code fasse tout autre chosecar vous aurez chang de compilateur
(portage vers une autre cible) ou parce que lanouvelle version de
votre compilateur favori aura dcid de grer cet aspect d'une
faontoute diffrente. Le code prsent ci-dessus a un comportement
indtermin.
La rgle prcdente s'tend de faon plus globale :
Un destructeur d'une classe utilise pour l'hritage public doit
tre virtuel s'il est publicou protg s'il est non virtuel.
L'entre de la F.A.Q. reprend cette problmatique : Pourquoi le
destructeur d'une classe de base doit trepublic et virtuel ou protg
et non virtuel ?
Le code prcdent peut alors soit se dcliner :
Interdire la destruction polymorphe :#include
struct base{protected: ~base() { std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 22 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Autoriser la destruction polymorphe : { std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 23 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Hritage prive : base *pb = new derived; // Erreur la compilation
! return 0;}
Si une classe ne dfinit pas explicitement de destructeur, alors
le compilateur cr un destructeur implicite qui secontente d'appeler
le destructeur de chaque membre de la classe ainsi que le
destructeur de chaque classe de base.Le destructeur implicitement
cr est public et non virtuel. Conclusion, il est potentiellement
dangereux si la classedoit servir de classe de base pour un
hritage. L'hritage est donc un des rares cas o il faut dfinir un
constructeurexplicite mme si celui-ci est trivial (c'est dire
vide).
Le destructeur implicite cr par le compilateur tant public et
non virtuel, une classedestine l'hritage doit dfinir un destructeur
explicite public et virtuel ou protg et nonvirtuel.
Un destructeur public DOIT tre virtuel :struct base{ virtual
~base(){} // dfinition obligatoire si base va servir pour un
hritage public !};
C++0x : le mot-cl =default est introduit dans la future norme
C++0x. Cela permet dedfinir une fonction dans la classe mais sans
lui donner d'implmentation explicite. Lecompilateur gnre une
implmentation implicite telle que dfinie par la norme. Pourles
destructeurs triviaux (c'est dire, ne faisant explicitement rien),
cette nouveaut estl'idal :
Destructeur virtuel par dfaut en C++0x :struct base{ virtual
~base()=default;// utilise la dfinition par dfaut // pour le
destructeur virtuel de base};
struct derived : public base{ virtual ~derived()=default; //
utilise la dfinition par dfaut // pour le destructeur virtuel de
derived};
Destructeur et constructeur sont les deux seules fonctions en
C++ dont l'appel vers la classe de base est automatique.Pour
prendre l'exemple du destructeur, lorsque le destructeur d'une
classe drive est termin, le destructeur de laclasse de base est
automatiquement appel sans avoir l'expliciter dans l'implmentation
du destructeur :
Appel en cascade des destructeurs virtuels ou pas :struct base{
virtual ~base() { std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 24 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Appel en cascade des destructeurs virtuels ou pas : derived d;
return 0;} // A la sortie, le destructeur derived::~derived est
appel
Le fait que le destructeur soit virtuel ne change pas ce
comportement. Le destructeur de la classe de base continued'tre
appel. La seule chose qui change est le premier appel dans le cadre
d'une destruction polymorphe : celui dutype dynamique de l'objet si
le destructeur est virtuel (OK), indtermin sinon.
VI - Seconde consquence : inlining de fonctions et fonctions
virtuelles
Le compilateur peut choisir d'inliner une fonction, c'est dire
de remplacer l'appel de celle-ci en recopiant directementle code
correspondant la fonction appele au niveau de la fonction appelante
(9) . C'est une optimisation quipermet d'acclrer le code puisqu'un
appel de fonction (qui est toujours coteux) est vit (10) . En C++,
deux faonspermettent de dfinir une fonction inline : en ajoutant le
mot-cl inline devant la fonction ou en la dfinissant dansla
dclaration de la classe :
Fonctions inline :struct my_struct{ inline void function_1();
void function_2() { // ... } protected : // ne plus oublier
maintenant de dfinir son destructeur // selon la politique souhaite
! ~my_struct() {} };
void my_struct::function_1(){ // ...}
int main(){ my_struct var; var.function_1(); // l'appel est
remplac par le corps de la fonction var.function_2(); // l'appel
est remplac par le corps de la fonction return 0;}
Quel rapport avec le type statique, dynamique et les fonctions
virtuelles ? Et bien, c'est le compilateur qui inline unefonction
donc seuls les appels rsolus la compilation peuvent faire l'objet
de cette optimisation. Si nous reprenonsle tableau indiquant le
moment de la rsolution d'un appel, les cas o le compilateur peut
inliner apparaissentimmdiatement :
(9) Pour tre exact, en l'absence de directive de compilation
explicite, le compilateur dcide seul si l'appel est inlinou pas
ventuellement sans tenir compte des desiderata du codeur. En fait
le mot cl inline a une autre utilitplus technique : dtourner la
rgle O.D.R. (One Definition Rule) en particulier pour les
templates.
(10)inline et rapidit ne sont pas aussi mcaniques qu'il y parat
: voir l'entre de la F.A.Q. Les fonctions inlineamliorent-elles les
performances ?
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 25 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
fonctionnon virtuelle
fonction virtuelle
appel sur un objet COMPILATION :inline possible
COMPILATION :inline possible
appel sur unerfrence ou unpointeur
COMPILATION :inline possible
EXCUTION : inlineimpossible
Deux consquences apparemment contradictoires :
Il est inutile de dclarer inline une fonction virtuelle.
En effet, cela ne sert rien si la fonction est appele avec une
rfrence ou un pointeur puisque la rsolution del'appel se fait
l'excution. Outre que c'est inutile, il est recommand de ne pas le
faire pour ne pas laisser croire un relecteur du code que cela
pourrait l'tre et accessoirement pour montrer ce relecteur que vous
avez comprisce mcanisme.
Une fonction virtuelle peut tre inline par le compilateur si
elle est appele dans uncontexte non polymorphe.
Cela correspond la premire ligne et la seconde colonne de notre
tableau : l'appel d'une fonction virtuelle sur unobjet par valeur
peut trs bien tre inlin puisque l'appel est rsolu la compilation
:
Fonction virtuelle inline :struct base{ virtual ~base(){}
virtual void function() { // ... }};struct derived : public base{
virtual void function() {// spcialisation // ... }};
int main(){ base b; b.function(); // peut tre inlin
derived d; d.function(); // peut tre inlin
base &rd = d; rd.function(); // NE peut PAS tre inlin
derived &rd2 = d; rd2.function(); // NE peut PAS tre
inlin
return 0;}
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 26 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
VII - Quand est construit le type dynamique ou quel est l'impact
des appels de fonctionsvirtuelles dans un constructeur ?
Le type dynamique d'un objet en cours de construction est celui
du constructeur en cours d'excution et non de l'objetrellement
construit.Reprenons l'ordre de construction d'un objet tel que nous
le trouvons prsent dans la F.A.Q. : Dans quel ordresont construits
les diffrents composants d'une classe ?
le constructeur des classes de base hrites virtuellement en
profondeur croissante et de gauche droite ; le constructeur des
classes de base hrites non virtuellement en profondeur croissante
et de gauche
droite ; le constructeur des membres dans l'ordre de leur
dclaration ; le constructeur de la classe.
Droulons l'exemple suivant :
Type dynamique et construction d'un objet :#include
struct base{ base() { std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 27 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Illustrons avec un peu de code :
Appel de fonctions virtuelles dans un constructeur :#include
struct base{ base() { init(); } virtual void init() {
std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 28 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Appeler une fonction virtuelle dans un constructeur n'appelle
pas la fonction la plusspcialise de l'objet en cours de
construction mais celle disponible pour le constructeuren cours
d'excution !
VIII - Que devient le type dynamique lors de la destruction de
l'objet ou peut-on appeler desfonctions virtuelles dans un
destructeur ?
Le problme est symtrique lors de l'appel du destructeur car les
destructeurs sont appels dans l'ordre inverse del'appel des
constructeurs : le type dynamique dans le destructeur concide,
comme dans le constructeur, avec letype statique du destructeur en
train d'tre droul. Et donc la fonction virtuelle appele est celle
disponible pour ledestructeur en cours d'excution.Soit en partant
du code suivant :
Type dynamique et destruction d'un objet :#include
struct base{ virtual ~base() { exit(); } virtual void exit() {
std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 29 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Destructeur encours d'excution
type dynamique Spcialisationappele
derived_2::~derived_2 derived_2
derived_2::exitderived_1::~derived_1 derived_1
derived_1::exitbase::~base base base::exit
Nous pouvons donc tirer une conclusion similaire pour la
destruction d'un objet celle de la construction :
Appeler une fonction virtuelle dans un destructeur n'appelle pas
la fonction la plusspcialise de l'objet en cours de destruction
mais celle disponible pour le destructeur encours d'excution !
IX - Construction, destruction, fonctions virtuelles et
multithreading
Les constructions et les destructions ne sont pas des oprations
atomiques. Par consquent, commencer utiliserun objet dans un fil
d'excution T1 pendant qu'il est construit dans un fil d'excution T2
peut rvler des surprises.En particulier, l'appel d'une fonction
virtuelle peut ne pas correspondre l'appel attendu si le type
dynamique n'estpas correctement initialis. Il en va de mme pendant
la phase de destruction. Un objet en train d'tre dtruit voit
sontype dynamique modifi lors de la remont des appels des
destructeurs. Ces situations de comptitions doivent treanticipes
lors de l'utilisation de l'objet sous peine de comportement
indtermin.
X - Une vision plus avance des appels de fonctions virtuelles
dans les constructeurs etdestructeurs
En fait, si l'approche type dynamique/type statique permet de
facilement comprendre que l'appel d'une fonctionvirtuelle dans un
constructeur ou un destructeur ne provoque pas l'appel de la
spcialisation de la classe la plusdrive de l'objet en cours de
construction (resp. de destruction), la ralit est plus subtile.En
effet, la norme dit juste que l'appel d'une fonction virtuelle dans
un constructeur (resp. destructeur) doit tre rsolupar l'appel de la
spcialisation de la classe en train d'tre construite (et non de
l'objet en train d'tre construit) oud'une de ses classes de
bases.Cette petite nuance permet certain compilateur d'optimiser
les appels directs des fonctions virtuelles dans lesconstructeurs
et les destructeurs. En effet, au moment de la compilation du
constructeur (resp. destructeur), lecompilateur connat la classe en
train d'tre construite (resp. dtruite) et toutes ses classes de
base. Cet appel peutds lors tre trait par le compilateur comme il
le ferait avec une fonction non virtuelle en utilisant une
rsolutionstatique. Et c'est bien le comportement observ avec un
compilateur comme Visual C++ Express 2008 ou GCC 4.4.1(MinGW) (11)
. Les appels directs des fonctions virtuelles dans le constructeur
et le destructeur sont identiques avecces compilateurs ceux de
fonctions non virtuelles. Il s'agit d'une optimisation de certains
compilateurs et non dela rgle gnrale !
Certains compilateurs optimisent les appels directs des
fonctions virtuelles dans leconstructeur ou le destructeur en
utilisant une rsolution statique de cet appel.
Optimisation de l'appel direct d'une fonction virtuelle dans le
constructeur/destructeur :struct base{ base() { do_init(); //
certains compilateurs traitent cet appel comme une fonction non
virtuelle } virtual void do_init() { std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 30 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Optimisation de l'appel direct d'une fonction virtuelle dans le
constructeur/destructeur : }
virtual ~base(){}};
Bien sr ceci n'est pas possible lorsque la fonction virtuelle
est appele dans le constructeur (ou le destructeur) defaon
indirecte :
Optimisation non ralise pour un appel indirect :struct base{
base() { init(); } void init() { do_init(); } virtual void
do_init() { std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 31 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Diffrences dans la rsolution dynamique entre la
construction/destruction et pendant la vie 'normale' de l'objet :{
std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 32 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Une classe dfinit une fonction virtuelle pure quand elle
souhaite indiquer que les classes drives DOIVENTspcialiser cette
fonction virtuelle pour pouvoir tre instancie. Une classe drivant
d'une classe abstraite etspcialisant les fonctions virtuelles pures
est appele classe concrte. Une classe concrte peut alors
treinstancie :
Seule une classe concrte peut tre instancie :struct abstract{
virtual void pure_function()=0; virtual ~abstract(){}};struct
concrete : public abstract{ virtual void pure_function() {}};int
main(){ concrete b; // OK return 0;}
Comme les fonctions virtuelles pures doivent tre spcialises par
les classes concrtes, il est possible de ne pasdfinir
d'implmentation pour une fonction virtuelle pure dans la classe
abstraite :
Les fonctions virtuelles pures peuvent ne pas tre dfinies dans
la classe abstraite :struct abstract{ virtual void
pure_function()=0; // pas de dfinition virtual void function(); //
doit tre dfinie virtual ~abstract(){}};struct concrete : public
abstract{ virtual void pure_function() {} // la dfinition dans la
classe concrte peut suffire // A noter que la spcialisation de la
fonction // dans la classe concrte DOIT tre dfinie virtual void
function(){} // la dfinition dans la classe ne suffit pas pour //
une classe virtuelle non pure. Elle doit aussi tre dfinie dans la
// classe de base};
int main(){ concrete c; return 0;}//Erreur l'dition de lien :
abstract::fonction non dfinie
Enfin, si une classe abstraite veut dfinir une fonction
virtuelle pure alors elle doit le faire en dehors de la
dclarationde la fonction :
Dfinition d'une fonction virtuelle pure l'extrieur de la
dclaration de la classe :struct abstract{ virtual void
pure_function()=0; virtual ~abstract(){}};void
abstract::pure_function(){// dfinition de la fonction}
Visual C++ compile une fonction virtuelle pure dfinie dans la
classe abstraite mais c'est un cart la norme. Le codesuivant
compile avec Visual C++ mais pas GCC :
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 33 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Ecart de Visual C++ :struct abstract{ virtual void
pure_function()=0 {// Compile avec Visual C++ mais pas avec GCC }
virtual ~abstract(){}};
S'il est impossible d'instancier une classe abstraite, rien en
revanche n'interdit d'utiliser un pointeur ou une rfrencevers une
classe abstraite :
Pointeur et rfrence vers une classe abstraite :struct abstract{
virtual void function()=0; virtual ~abstract(){}};struct concrete :
public abstract{ virtual void function(){}};
int main(){ concrete c; abstract &ra = c; abstract *pa = new
concrete; // On n'oublie pas notre destructeur virtuel... delete
pa; return 0;}
Enfin, les plus attentifs auront dj dduit cette consquence de
l'impossibilit d'instancier une classe abstraite :
Le type dynamique d'une variable ne peut jamais tre une classe
abstraite en dehors duconstructeur ou du destructeur.
En effet, le type dynamique est le type de l'objet rellement
utilise l'excution du programme. Or ne pouvantinstancier de classe
abstraite, l'objet rellement utilise sera toujours une classe
concrte drive de la classeabstraite. Nous avons vu que les deux
seuls cas chappant cette rgle sont le constructeur et le
destructeur danslesquels types dynamique et statique concident.En
revanche, le type statique d'une variable peut tre celui d'une
classe abstraite. this l'intrieur d'une fonction dela classe
abstraite est un cas trivial o le type statique est celui de la
classe abstraite. Mais il en va de mme pourles rfrences et les
pointeurs de classe abstraite :
Types statiques et dynamiques dans les classes abstraites et
concrtes :struct abstract{ virtual void pure_function()=0; void
a_function() {// type statique == abstract // type dynamique ==
type de la classe concrte drive } virtual ~abstract() {}};struct
concrete : public abstract{ virtual void pure_function() {}};int
main(){ concrete b;
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 34 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Types statiques et dynamiques dans les classes abstraites et
concrtes : abstract &rb = b; // le type statique de rb est
abstract, le type dynamique est concrete abstract *pb = &b; //
le type statique de pb est abstract, le type dynamique est concrete
return 0;}
XI-B - Appel d'une fonction virtuelle pure
L'appel d'une fonction virtuelle pure suit toujours les mmes
rgles que celle d'une fonction virtuelle classique. Il estpossible
de l'appeler depuis n'importe quelle autre fonction :
Rsolution dynamique de l'appel d'une fonction virtuelle pure
:#include
struct base{ void call_function() { function(); } virtual void
function()=0; virtual ~base(){}
};struct derived :public base{ virtual void function() {
std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 35 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Forcer l'appel la dfinition de la classe de base :{ virtual void
function() const =0; virtual ~base(){}};void base::function()
const{ std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 36 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
On peut se demander pourquoi devrait-on avoir besoin de dfinir
une fonction virtuelle pure dans la classe abstraitesi elle n'est
jamais implicitement appele par la rsolution dynamique. Mon opinion
est qu'une telle dfinition estsouvent une erreur de conception.
Voyons les diffrents cas qui semblent ncessiter une dfinition dans
la classeabstraite et regardons comment les contourner :
La classe de base propose un comportement par dfaut : si tel est
le cas, alors c'est que la classe drivepeut trs bien n'utiliser que
ce comportement par dfaut et ne pas avoir de comportement
spcifique. Dans cecas, la fonction peut tre uniquement virtuelle
(pas pure), les classes drives n'ayant pas de comportementspcifique
ne spcialisent pas la fonction virtuelle.
Avec une fonctionvirtuelle pure :
Une autre approche :
Une fonction virtuelle pure avec uncomportement par dfaut :
pourquoifaire ?#include
struct base{ virtual void function()=0; virtual ~base(){}};void
base::function(){ std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 37 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Une fonction virtuelle pure avec uncomportement factoris :
pourquoifaire ? std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 38 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Une fonction virtuelle pure avec uncomportement obligatoire : le
patternN.V.I. ? std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 39 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Destructeur virtuel par dfaut en C++0x :base::~base()=default;
// dfinition l'extrieur de la classe
XI-D - Appel d'une fonction virtuelle pure dans le
constructeur/destructeur d'une classeabstraite
La norme est catgorique pour les appels des fonctions virtuelles
pures dans le constructeur ou le destructeur :
L'appel d'une fonction virtuelle pure dans le constructeur ou le
destructeur d'une classeabstraite produit un comportement indtermin
!
Cette rgle tombe sous le bon sens. Rappelez-vous une fonction
virtuelle pure peut ne pas avoir de dfinition dansune classe
abstraite. Ds lors, la rgle pour les fonctions virtuelles stipulant
que c'est la fonction dfinie dans leconstructeur en cours de
construction qui est appele ne peut tre tendue puisque cette
dfinition peut ne pasexister. Afin de ne pas crer d'ambigut, la
rgle ci-dessus qui a l'avantage de la simplicit a t introduite dans
lanorme : l'appel d'une fonction virtuelle pure dans une classe
abstraite provoque un comportement indtermin.Si cette rgle a
l'avantage de la simplicit est l'inconvnient qu'un comportement
indtermin peut rvler dessurprises. Regardons ainsi comment cette
rgle est mise en oeuvre dans 2 compilateurs diffrents : Visual C++
etGCC.
Prenons un premier exemple simple : une fonction virtuelle pure
sans dfinition dans la classe abstraite appeledirectement dans le
constructeur (le comportement est le mme si l'appel a lieu dans le
destructeur)
Appel direct d'une fonction virtuelle pure sans dfinition dans
un constructeur#include struct base{ base() { do_init(); } virtual
void do_init()=0; virtual ~base(){}};
struct derived : public base{ derived() { } virtual void
do_init() { std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 40 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Cette erreur l'dition de lien se comprend au regard de
l'optimisation des appels des fonctions virtuelles dans
leconstructeur (resp. destructeur). Cette optimisation permet une
rsolution statique de l'appel par le compilateur. Ilinsre donc tout
simplement un appel vers base::do_init dans le constructeur. Or
comme cette fonction n'est dfinienulle part, l'dition de lien
choue. C'est bien un appel vers base::do_init qui est tent et non
vers derived::do_init carla rgle stipule bien que le compilateur
doit considrer le constructeur en cours d'excution.
Un second exemple plus embtant. Considrons maintenant l'appel
indirect d'une fonction virtuelle pure dans leconstructeur (comme
toujours, le comportement est le mme dans le destructeur) :
Appel indirect d'une fonction virtuelle pure sans dfinition dans
un constructeur#include struct base{ base() { init(); } void init()
{ do_init(); } virtual void do_init()=0; virtual ~base(){}};
struct derived : public base{ derived() { } virtual void
do_init() { std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 41 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Appel direct d'une fonction virtuelle pure avec dfinition dans
un constructeur{ std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 42 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Ici, nous retrouvons le comportement observ dans le second
exemple savoir un plantage l'excution indiquantl'appel d'une
fonction virtuelle pure dans le constructeur :
GCC : pure virtual method called> Visual C++ : R6025- pure
virtual function callSoit pour rsumer, nous avons avec Visual C++
et GCC les comportements suivants pour un appel de
fonctionvirtuelle pure dans un constructeur ou un destructeur :
Fonction virtuellepure sans dfinition
Fonction virtuellepure avec dfinition
appel direct erreur de lien OKappel indirect erreur l'excution
erreur l'excution
Ceci avait pour seul but de montrer qu'un comportement dclar
comme indtermin par la norme se rvleeffectivement vari et subtil.
Vos codes ne doivent pas transiger avec cette rgle lmentaire :
Une classe abstraite ne doit pas appeler de fonction virtuelle
pure directement ouindirectement dans son constructeur ou son
destructeur.
XI-E - Fonctions virtuelles pures et constructeur/destructeur
des classes concrtes
A partir du moment o une fonction virtuelle pure reoit une
spcialisation dans une classe concrte, alors elle secomporte comme
une fonction virtuelle classique. La fonction appele est celle du
constructeur en train de s'excuteret non de l'objet en train d'tre
construit :
Appel d'une fonction virtuelle pure dans une classe concrte
l'ayant spcialise#include struct base{ base() {} void init() {
do_init(); } virtual void do_init()=0; virtual ~base(){}};
struct derived : public base{ derived() { init(); } virtual void
do_init() { std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 43 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Appel d'une fonction virtuelle pure dans une classe concrte
l'ayant spcialise};
int main(){ derived_2 d; return 0;}
Si init est bien une fonction de la classe abstraite base,
l'appel effectif init se fait bien dans le constructeur des
classesconcrtes derived et derived_2. Il ne faut pas tenir compte
de la classe qui fait l'appel mais bien du constructeur(resp.
destructeur) qui est en train de se drouler.
Il est possible d'appeler une fonction virtuelle pure dans le
constructeur ou le destructeurd'une classe concrte l'ayant
spcialise. Le comportement est le mme que pour unefonction
virtuelle classique : c'est la version du constructeur/destructeur
en train d'treexcut qui est appele et non celle de l'objet en train
d'tre construit.
XII - L'operateur d'affectation : operator=
L'oprateur d'affectation peut tre dclar virtuel comme toutes les
autres fonctions. On distingue deux oprateursd'affectation :
l'oprateur de 'conversion' et l'oprateur de 'copie'.Le premier
prend en paramtre un type diffrent de la classe pour laquelle il
est dfini :
Oprateur = avec smantique de conversion :struct a_type{
a_type& operator=(int i_);};
Il permet d'initialiser tout ou partie d'un objet d'un type donn
avec la valeur d'un autre type. Dfinir comme virtuel etle
spcialiser au besoin dans les classes drives ne posent pas de
problmes particuliers pour ce type d'affectation.L'oprateur
d'affectation avec une smantique de copie prend en argument le mme
type que la classe pour laquelleil est dfini :
Oprateur = avec smantique de copie :struct a_type{ a_type&
operator=(a_type rhs_);};
D'ailleurs, pour ne laisser aucune confusion sur le fait qu'il
s'agit bien d'un oprateur de copie, on utilise souventl'idiome copy
and swap :
Copy and swap :struct a_type{ a_type& operator=(a_type rhs_)
// la copie est fait lors du passage de l'argument {
std::swap(membre_1, rhs_.membre_1); // swap des membres
std::swap(membre_2, rhs_.membre_2); /*[...] */ std::swap(membre_n,
rhs_.membre_n); return *this; }};
L'oprateur d'affectation de copie d'une classe virtuelle ne
spcialise pas celui de la classe de base :
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 44 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Ceci n'est pas une spcialisation :#include
struct base{ virtual base& operator=(base) { std::cout
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 45 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Spcialiser l'oprateur de base ?
int main(){ base b1; base b2; b1 = b2; // base::operator=
derived d1; derived d2; d1 = d2; // derived::operator= (1)
base &rd1 = d1; base &rd2 = d2; rd1 = rd2; //
base::operator= (2) rd1 = d1; // base::operator= (3) return 0;}
Certes, maintenant la classe spcialise bien l'oprateur dfini
dans la classe de base. Mais cette solution ne peuttre
satisfaisante. En effet, dans le cas (1) et dans les cas (2) et
(3), on souhaite affecter un objet derived un autreobjet derived.
Mais passer par une rfrence de la classe de base nous fait perdre
l'information sur le type effectifde l'argument. On a donc deux
appels diffrents selon (1) ou selon (1) et (2).Quelle conclusion
tirer ? En fait l'oprateur d'affectation de copie met en vidence un
problme plus fondamentalen conception objet concernant les classes
servant l'hritage et utilisant les fonctions virtuelles. Ces
classesont souvent une smantique d'entit. Et une smantique d'entit,
comme on le voit, se marie mal avec unesmantique de copie.
XIII - Le retour covariant des fonctions virtuelles
Une fonction virtuelle dans la classe drive doit prsenter la mme
signature (mme nom, mme nombre deparamtres, mme type des paramtres,
mme constance de la fonction) pour spcialiser la fonction dfinie
dansla classe de base. Or le type de retour de la fonction ne fait
pas partie de la signature de la fonction. Il devient dslors
lgitime de se poser la question suivante : une fonction virtuelle
peut elle avoir un type de retour diffrent de laclasse de base ? La
rponse est en deux temps : non mais oui.Non : si les deux types de
retour n'ont rien en commun alors c'est une erreur :
La spcialisation d'une fonction virtuelle ne peut retourner un
type vraiment diffrent :struct base { virtual int function() {
return 0; }};struct derived : public base{ virtual double
function() // Erreur ! { return 0.0; }};
Mais oui condition de respecter les contraintes suivantes :
le retour doit tre par pointeur ou par rfrence ; ET le type
retourn dans la classe drive doit driver directement ou
indirectement et sans ambigut du
type retourn dans la classe de base ; ET les types retourns
doivent avoir la mme constance ou celui de la classe drive doit
avoir une
constance plus faible.
On parle alors d'un retour covariant car le retour de la
fonction virtuelle varie en mme temps que l'hritage :
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 46 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Retour covariant :struct base_return{};
struct base { virtual base_return& function();};
struct derived_return : public base_return{};
struct derived : public base{ virtual derived_return&
function();};
Le retour est dit covariant car une spcialisation dans une
classe C ne peut retourner qu'un type gal ou drivant dela fonction
virtuelle la plus spcialise de son arbre d'hritage :
Retour covariant : types retour et types de la classe suivent un
mme se spcialisent en mme temps
Ce qui se traduit en code :
Retours covariants corrects :struct base_return{};struct
derived_1_return : public base_return{};struct derived_2_return :
public derived_1_return{};
struct base { virtual base_return& function();};
struct derived_1 : public base{
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 47 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Retours covariants corrects : virtual derived_1_return&
function();};
struct derived_2 : public derived_1{ virtual
derived_2_return& function();};
Erreur : le type retour n'est pas covariant
Ce qui se traduit en code :
Un type de retour non covariant est une erreur :struct
base_return{};struct derived_1_return : public base_return{};struct
derived_2_return : public derived_1_return{};
struct base { virtual base_return& function();};
struct derived_1 : public base{ virtual derived_2_return&
function();};
struct derived_2 : public derived_1{ virtual
derived_1_return& function(); // Erreur : le type retour n'est
pas covariant ! virtual base& function(); // Erreur : le type
retour n'est pas covariant ! virtual derived_2_return&
function(); // OK};
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 48 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Erreur : le type retour n'est pas covariant
Ce qui se traduit en code :
Un type de retour non covariant est une erreur :struct
base_return{};struct derived_1_return : public base_return{};struct
derived_3_return : public base_return{};
struct base { virtual base_return& function();};
struct derived_1 : public base{ virtual derived_1_return&
function();};
struct derived_2 : public derived_1{ virtual
derived_3_return& function(); // Erreur : le type retour n'est
pas covariant ! virtual derived_1_return& function(); //
OK};
La constance du retour peut tre perdue en chemin mais pas
rajoute :
La constance du retour peut tre perdue :struct
base_return{};
struct base { virtual base_return const & function_1();
virtual base_return & function_2();};
struct derived_1 : public base{ virtual base_return &
function_1(); // OK virtual base_return const & function_2();
// Erreur};
Pour comprendre ces rgles de covariance, il faut se rappeler que
la fonction de la classe drive peut treappele avec un pointeur ou
une rfrence d'un type statique de la classe de base. Pour que le
compilateur puissecorrectement vrifier les types la compilation
(donc pour les classes de base), les types retourns l'excution(donc
par les classes drives) doivent prsenter une certaine cohrence.
C'est pour cela qu'une fonction spcialisene peut retourner qu'un
type qui puisse se substituer la version la plus spcialise de ses
classes de base.
-
Les fonctions virtuelles en C++ : Types statiques et types
dynamiques par 3DArchi
- 49 -Copyright 2009 3DArchi. Aucune reproduction, mme
partielle, ne peut tre faite de ce site et de l'ensemble de son
contenu : textes, documents, images, etc. sans l'autorisation
expresse del'auteur. Sinon vous encourez selon la loi jusqu' trois
ans de prison et jusqu' 300 000 de dommages et intrts. Cette page
est dpose la SACD.
http://apais.developpez.com/tutoriels/c++/fonctions-virtuelles-en-cpp/
Ncessit du retour covariant :
struct base { virtual base_return const & function_1();
virtual base_return & function_2();};
void a_function_handling_base(base&b_){ base_return const
& cr = b_function_1();// cr ne peut rfrencer que quelque //
chose qui se substitue base_return const sans problme : une classe
// drive et soit constante soit non constante.
base_return & r2 = b_function_2();// r2 ne devrait pas p