Download - Fonctions Virtuelles en Cpp hh

Transcript
  • 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