Apprendre C++ avec Qt : Annexe 5 Run-Time Type Information
Le RTTI est un dispositif qui permet de dterminer le type de l'objet dont l'adresse est contenue dans un pointeur sur une classe de base (il peut lgitimement s'agir d'une instance de la classe de base ou d'une instance de n'importe laquelle des classes drives de celle-ci).
Etant donn qu'un pointeur sur une classe de base ne doit normalement servir qu' appeler des fonctions de cette classe (qui peuvent, ventuellement, tre virtuelles et redfinies dans les classes drives), il n'est habituellement pas ncessaire de connatre le type exact de l'objet qu'il dsigne (la virtualit des fonctions redfinies suffit garantir que c'est le code adapt au type concern qui sera excut). Nous pouvons donc conclure que
Un programme "normal" ne fait jamais appel au RTTI.
S'il s'agit de dbuguer un programme ou simplement d'explorer le fonctionnement des fonctions virtuelles, le recours au RTTI peut cependant s'avrer instructif, ou mme trs utile. 1 - L'oprateur t ypei d et la classe t ype_i nf o L'oprateur t ypei d( ) renvoie une rfrence une constante de type t ype_i nf o correspondant au type de l'objet auquel il est appliqu.
Typiquement, cet objet est dsign en drfrenant un pointeur sur une classe de base.
Une valeur de type t ype_i nf o dcrit un type sous une forme utilisable par un programme. Il est ainsi possible de comparer deux valeurs (avec les oprateurs == et ! =) et d'obtenir une chane de caractres correspondant au nom utilis dans le code source pour dsigner le type. Ainsi, aprs
cl ass CBase 1 { 2 publ i c: 3 vi r t ual voi d m_f ( ) {} 4 }; 5
cl ass CDer i vee : publ i c CBase 6 { 7 publ i c: 8 i nt m_pr opr e; 9 }; 10
l'excution du fragment de code suivant
CDer i vee unD; 1 CBase * pt r = &unD; 2 const t ype_i nf o & l eType = t ypei d( *pt r ) ; 3 QSt r i ng l aCl asse = l eType. name( ) ; 4
placera dans la variable l aCl asse la chane cl ass CDer i vee
L'utilisation de la classe t ype_i nf o exige la prsence d'une directive #i ncl ude " t ypei nf o. h"
Si vous utilisez Visual C++ 6.0, vous devez aussi cocher la case "Enable RTTI" dans l'onglet "C/C++" (category "C++ langage") de vos "Projects settings".
Notez galement que
L'oprateur t ypei d( ) ne donne rellement le type de l'objet point que si la classe de base concerne possde au moins une fonction virtuelle.
Document du 23/08/05 - Retrouvez la version la plus rcente sur http://www.up.univ-mrs.fr/wcpp C++ - Annexe 5 RTTI 2/2
2 - L'oprateur dynami c_cast < > Lorsqu'un pointeur sur une classe de base contient l'adresse d'une instance d'une classe drive, l'oprateur dynami c_cast < >permet de convertir cette valeur en un pointeur sur la classe drive en question (ce qui permettra d'accder aux membres propres qu'elle dfinit). Si l'objet point n'est pas du type attendu (il peut s'agir d'une simple instance de la classe de base, par exemple), la valeur produite par dynami c_cast < >est nulle.
C'est cette dernire caractristique qui justifie l'existence de dynami c_cast < >. L'usage d'un st at i c_cast < >ou d'un r ei nt er pr et _cast < >permet en effet d'obtenir n'importe quelle conversion entre type de pointeurs, mais sans aucune vrification de cohrence
/ / l es cl asses CBase et CDer i vee sont supposes df i ni es comme pr cdemment 1 voi d f onct i onSauvage( CBase *pt r Base) 2 { 3 CDer i vee * pt r Der i vee = dynami c_cast <CDer i vee *> ( pt r Base) ; 4 i f ( pt r Der i vee ! = NULL) 5 pt r Der i vee- >m_pr opr e = 4; 6 } 7
Si la fonction ci-dessus permet d'illustrer l'usage de l'oprateur dynami c_cast < >, elle permet galement d'expliquer pourquoi ce type de conversion (connue sous le nom de "downcasting", parce qu'elle permet de "descendre" dans la hirarchie des classes) n'est gnralement pas souhaitable.
Remarquons tout d'abord que cette fonction, qui n'est membre ni de CBase ni de CDer i vee, prtend accder une variable membre. Ce simple fait est dj une hrsie, puisque l'interface d'une classe ne doit comporter que des fonctions membre.
En quoi la situation serait-elle diffrente s'il s'agissait d'appeler une fonction membre de CDer i vee et non d'accder une de ses variables ? Dans ce contexte, la simple vocation d'une fonction membre appelle la question "Pourquoi n'est-elle pas virtuelle ?"
Si la classe CBase comporte une fonction virtuelle nomme r emi seEnEt at I ni t i al ( ) , par exemple, cette fonction peut tre redfinie par les classes drives, qui sont les seules vraiment savoir ce qu'il convient de faire avec leurs membres propres.
La version redfinie dans CDer i vee se chargera, par exemple, d'affecter la valeur 4 la variable m_pr opr e.
Le code de la f onct i onSauvage( ) devient alors
voi d f onct i onSauvage( CBase *pt r Base) 1 { 2 pt r Base- >r emi seEnEt at I ni t i al ( ) ; 3 } 4
Non seulement ce code est plus court et plus clair, mais il fait quelque chose dont la version initiale de la fonction tait bien incapable : il traite correctement le cas de toutes les classes drives de CBase, qu'elles soient prsentes ou venir.
Il est concevable que la fonction propre qui doit tre excute soit tellement spcifique la classe drive concerne que la cration d'une fonction virtuelle dans la classe de base semble franchement draisonnable. Une autre piste doit alors tre explore : pourquoi la f onct i onSauvage( ) n'est-elle pas elle-mme une fonction virtuelle de la classe de base ? Elle pourrait alors tre facilement redfinie par les classes ncessitant des traitements trs spcifiques (et par elles seules), et ces redfinitions pourraient, le cas chant, appeler explicitement la version de base de la fonction, pour viter de s'occuper d'autre chose que de la spcificit qui justifie leur existence.
Nous pouvons donc conclure que
Un programme "normal" ne fait jamais appel l'oprateur dynami c_cast < >.