• Aucun résultat trouvé

#include <typeinfo>

using namespace std;

class Base {

public:

virtual ~Base(void); // Il faut une fonction virtuelle // pour avoir du polymorphisme.

Chapitre 10. Identification dynamique des types

class Derivee : public Base {

Derivee* pd = new Derivee;

Base* pb = pd;

const type_info &t1=typeid(*pd); // t1 qualifie le type de *pd.

const type_info &t2=typeid(*pb); // t2 qualifie le type de *pb.

return 0 ; }

Les objetst1ett2sont égaux, puisqu’ils qualifient tous les deux le même type (à savoir, la classe Derivee). t2ne contient pas les informations de type de la classe Base, parce que le vrai type de l’objet pointé parpbest la classe Derivee.

Note : Notez que la classe type_info est définie dans l’espace de nommagestd::, réservé à la bibliothèque standard C++, dans l’en-têtetypeinfo. Par conséquent, son nom doit être précédé du préfixestd::. Vous pouvez vous passer de ce préfixe en important les définitions de l’espace de nommage de la bibliothèque standard à l’aide d’une directiveusing. Vous trouverez de plus amples renseignements sur les espaces de nommage dans le Chapitre 11.

On fera bien attention à déréférencer les pointeurs, car sinon, on obtient les informations de type sur ce pointeur, pas sur l’objet pointé. Si le pointeur déréférencé est le pointeur nul, l’opérateurtypeid lance une exception dont l’objet est une instance de la classe bad_typeid. Cette classe est définie comme suit dans l’en-têtetypeinfo:

class bad_typeid : public logic {

public:

bad_typeid(const char * what_arg) : logic(what_arg) {

Chapitre 10. Identification dynamique des types };

10.1.2. La classe type_info

Les informations de type sont enregistrées dans des objets de la classe type_info prédéfinie par le langage. Cette classe est déclarée dans l’en-têtetypeinfode la manière suivante :

class type_info {

public:

virtual ~type_info();

bool operator==(const type_info &rhs) const;

bool operator!=(const type_info &rhs) const;

bool before(const type_info &rhs) const;

const char *name() const;

private:

type_info(const type_info &rhs);

type_info &operator=(const type_info &rhs);

};

Les objets de la classe type_info ne peuvent pas être copiés, puisque l’opérateur d’affectation et le constructeur de copie sont tous les deux déclarésprivate. Par conséquent, le seul moyen de générer un objet de la classe type_info est d’utiliser l’opérateurtypeid.

Les opérateurs de comparaison permettent de tester l’égalité et la différence de deux objets type_info, ce qui revient exactement à comparer les types des expressions.

Les objets type_info contiennent des informations sur les types sous la forme de chaînes de caractères.

Une de ces chaînes représente le type sous une forme lisible par un être humain, et une autre sous une forme plus appropriée pour le traitement des types. Le format de ces chaînes de caractères n’est pas précisé et peut varier d’une implémentation à une autre. Il est possible de récupérer le nom lisible du type à l’aide de la méthodename. La valeur renvoyée est un pointeur sur une chaîne de caractères. On ne doit pas libérer la mémoire utilisée pour stocker cette chaîne de caractères.

La méthodebeforepermet de déterminer un ordre dans les différents types appartenant à la même hiérarchie de classes, en se basant sur les propriétés d’héritage. L’utilisation de cette méthode est toutefois difficile, puisque l’ordre entre les différentes classes n’est pas fixé et peut dépendre de l’implémentation.

10.2. Transtypages C++

Les règles de dérivation permettent d’assurer le fait que lorsqu’on utilise un pointeur sur une classe, l’objet pointé existe bien et est bien de la classe sur laquelle le pointeur est basé. En particulier, il est possible de convertir un pointeur sur un objet en un pointeur sur un sous-objet.

En revanche, il est interdit d’utiliser un pointeur sur une classe de base pour initialiser un pointeur sur une classe dérivée. Pourtant, cette opération peut être légale, si le programmeur sait que le pointeur pointe bien sur un objet de la classe dérivée. Le langage exige cependant un transtypage explicite.

Une telle situation demande l’analyse du programme afin de savoir si elle est légale ou non.

Chapitre 10. Identification dynamique des types

Parfois, il est impossible de faire cette analyse. Cela signifie que le programmeur ne peut pas certifier que le pointeur dont il dispose est un pointeur sur un sous-objet. Le mécanisme d’identification dyna-mique des types peut être alors utilisé pour vérifier, à l’exécution, si le transtypage est légal. S’il ne l’est pas, un traitement particulier doit être effectué, mais s’il l’est, le programme peut se poursuivre normalement.

Le C++ fournit un jeu d’opérateurs de transtypage qui permettent de faire ces vérifications dyna-miques, et qui donc sont nettement plus sûrs que le transtypage tout puissant du C que l’on a utilisé jusqu’ici. Ces opérateurs sont capables de faire un transtypage dynamique, un transtypage statique, un transtypage de constance et un transtypage de réinterprétation des données. Nous allons voir les différents opérateurs permettant de faire ces transtypages, ainsi que leur signification.

10.2.1. Transtypage dynamique

Le transtypage dynamique permet de convertir une expression en un pointeur ou une référence d’une classe, ou un pointeur sur void. Il est réalisé à l’aide de l’opérateur dynamic_cast. Cet opérateur impose des restrictions lors des transtypages afin de garantir une plus grande fiabilité :

il effectue une vérification de la validité du transtypage ;

il n’est pas possible d’éliminer les qualifications de constance (pour cela, il faut utiliser l’opérateur const_cast, que l’on verra plus loin).

En revanche, l’opérateurdynamic_castpermet parfaitement d’accroître la constance d’un type com-plexe, comme le font les conversions implicites du langage vues dans la Section 3.2 et dans la Section 4.7.

Il ne peut pas travailler sur les types de base du langage, sauf void *.

La syntaxe de l’opérateurdynamic_castest donnée ci-dessous : dynamic_cast<type>(expression)

oùtypedésigne le type cible du transtypage, etexpressionl’expression à transtyper.

Le transtypage d’un pointeur ou d’une référence d’une classe dérivée en classe de base se fait donc directement, sans vérification dynamique, puisque cette opération est toujours valide. Les lignes suivantes :

// La classe B hérite de la classe A : B *pb;

A *pA=dynamic_cast<A *>(pB);

sont donc strictement équivalentes à celles-ci : // La classe B hérite de la classe A : B *pb;

A *pA=pB;

Tout autre transtypage doit se faire à partir d’un type polymorphique, afin que le compilateur puisse utiliser l’identification dynamique des types lors du transtypage. Le transtypage d’un pointeur d’un objet vers un pointeur de type void renvoie l’adresse du début de l’objet le plus dérivé, c’est-à-dire

Chapitre 10. Identification dynamique des types l’adresse de l’objet complet. Le transtypage d’un pointeur ou d’une référence sur un sous-objet d’un objet vers un pointeur ou une référence de l’objet complet est effectué après vérification du type dynamique. Si l’objet pointé ou référencé est bien du type indiqué pour le transtypage, l’opération se déroule correctement. En revanche, s’il n’est pas du bon type,dynamic_castn’effectue pas le transtypage. Si le type cible est un pointeur, le pointeur nul est renvoyé. Si en revanche l’expression caractérise un objet ou une référence d’objet, une exception de type bad_cast est lancée.

La classe bad_cast est définie comme suit dans l’en-têtetypeinfo: class bad_cast : public exception

{

virtual const char* what(void) const throw();

};

Lors d’un transtypage, aucune ambiguïté ne doit avoir lieu pendant la recherche dynamique du type.

De telles ambiguïtés peuvent apparaître dans les cas d’héritage multiple, où plusieurs objets de même type peuvent coexister dans le même objet. Cette restriction mise à part, l’opérateurdynamic_cast est capable de parcourir une hiérarchie de classe aussi bien verticalement (convertir un pointeur de sous-objet vers un pointeur d’objet complet) que transversalement (convertir un pointeur d’objet vers un pointeur d’un autre objet frère dans la hiérarchie de classes).

L’opérateurdynamic_castpeut être utilisé dans le but de convertir un pointeur sur une classe de base virtuelle vers une des ses classes filles, ce que ne pouvaient pas faire les transtypages classiques du C. En revanche, il ne peut pas être utilisé afin d’accéder à des classes de base qui ne sont pas visibles (en particulier, les classes de base héritées enprivate).