18
Page de garde C++ e RTTI et les opérateurs de cas [email protected] Maîtrise d’informatique Février 2002

Page de garde

  • Upload
    eve

  • View
    34

  • Download
    0

Embed Size (px)

DESCRIPTION

C++ Le RTTI et les opérateurs de cast. Page de garde. [email protected]. Maîtrise d’infor matique Février 2002. Le RTTI. Le Run-Time Type Information (RTTI). Si la classe B étend la classe A , alors dans l’expression. A * ptr= new B();. - PowerPoint PPT Presentation

Citation preview

Page 1: Page de garde

Page de garde

C++Le RTTI et les opérateurs de cast

[email protected]

Maîtrise d’informatiqueFévrier 2002

Page 2: Page de garde

Le RTTI

Le Run-Time Type Information (RTTI)

Si la classe B étend la classe A, alors dans l’expression

A * ptr= new B();

A* est le type statique de ptr, information que le compilateur conserve.

B* est le type dynamique de ptr, information que le compilateur "oublie".

Comment connaître le type dynamique de ptr à l’exécution ?

Page 3: Page de garde

Le RTTI

Le Run-Time Type Information (RTTI)

Solution à la main :

class A { ... virtual bool IsB() const { return false ); ...}

class B: public A { ... virtual bool IsB() const { return true ); ...}

Implémentation fastidieuse et source potentielle d’erreurs.

Implique de mettre à jour la classe racine lors de l’ajout de classes dérivées.

Encombrement de la table des méthodes virtuelles.

Exécution a priori très rapide.

Page 4: Page de garde

Le RTTI

Le Run-Time Type Information (RTTI)

Solution C++ : le RTTI !

#include <typeinfo>

const type_info& typeid( expression_or_type )

/** égalité/inégalité stricte */int type_info::operator==( const type_info& rhs ) const;int type_info::operator!=( const type_info& rhs ) const;

/** nom human-readable */const char * type_info::name() const;

/** nom interne */const char * type_info::raw_name() const;

/** collating order (aucune indication hiérarchique !) */int type_info::before( const type_info& rhs ) const;

Page 5: Page de garde

Le RTTI

Le Run-Time Type Information (RTTI)

class A { ... };class B: public A { ... };

const A * const a= new A();const B * const b= new B();

typeid( a ).name() "class A const *" ????? const ?????typeid( *a ).name() "class A" ????? const ?????

typeid( a ) == typeid( a ) truetypeid( a ) == typeid( b ) false

Page 6: Page de garde

Le RTTI

Le Run-Time Type Information (RTTI)

Attention : l’utilisation de typeid sur des classes non polymorphes (i.e. sans fonctions virtuelles) pourrait renvoyer le type statique…

Pour pouvoir utiliser typeid sur toutes les classes, il est nécessaire de forcer le compilateur à ajouter des informations de type dynamique aussi sur les classes classes non polymorphes :

Visual C++ : /GR ou Project | Settings | C/C++ | C++ Language | Enable RTTI

g++ : ??????????

Pensez à déréférencer les pointeurs pour obtenir des informations sur le type de l’objet pointé, et pas sur le type du pointeur !

Si ptr est 0/NULL, typeid( *ptr ) lance l’exception std::bad_typeid.

Page 7: Page de garde

Les opérateurs de cast

Les opérateurs de cast

4 opérateurs de cast :

reinterpret_cast aucune vérification

static_cast vérification statique de plausibilité (downcast)

dynamic_cast vérification dynamique (downcast)

const_cast pour enlever const et/ou volatile

Syntaxe générale :

xxxxx_cast< TargetType >( expression )

Page 8: Page de garde

reinterpret_cast

reinterpret_cast

Correspond au cast de C (aucune vérification à la compilation, aucune vérification à l’exécution) :

reinterpret_cast< Carotte >( choux )

Les casts "à la C" sont toujours accepté en C++ :

(Carotte)choux

Mais, les casts étant une source notable d’erreurs, il est bon de pouvoir les énumérer avec un simple grep !

Page 9: Page de garde

static_cast

static_cast

Accepte uniquement de convertir un type en un type dérivé à la compilation ; aucune vérification à l’exécution :

static_cast< ChouxBlanc >( choux )

Refuse notamment de convertir des pointeurs en entiers longs :

static_cast< unsigned long >( apointer )

Utiliser reinterpret_cast, pas un cast "à la C" !

Page 10: Page de garde

dynamic_cast

dynamic_cast

Accepte uniquement de convertir un type en un type dérivé à la compilation avec vérification à l’exécution, en utilisant le RTTI :

dynamic_cast< ChouxBlanc >( choux )

En cas d’erreur (le type dynamique de l’expression n’est pas un type dérivé du type cible) :

Les conversions de pointeurs retournent 0/NULL

Les conversions de références lancent une exception std::bad_cast

dynamic_cast est très similaire aux casts (de références) en Java

Page 11: Page de garde

Upcasts et héritage multiple

Upcasts et héritage multiple

L’upcast en cas d’héritage multiple est ambigu :

const D * const d= new D();

static|dynamic_cast< const A* >( d ) // ambigu !

Il faut d’abord caster en B ou C, puis en A.

Page 12: Page de garde

Downcasts et héritage virtuel

Downcasts et héritage virtuel

Le downcast en cas d’héritage virtuel est ambigu :

const A * const a= new E();

static|dynamic_cast< const B* >( a ) // ambigu !

Il faut d’abord caster en E, puis en C ou D, puis en B.

Page 13: Page de garde

const_cast

const_cast

Supprime, à la compilation, le caractère const ou volatile des expressions :

(const_cast< AType& >( areference )).NonConstMethod();

Tout aussi alarmant concernant le design que d’utiliser mutable…

Page 14: Page de garde

Des casts contrôlés en mode debug

Des casts contrôlés en mode debug

Les casts sont malheureusement souvent nécessaires alors qu’ils sont sources d’erreurs !

static_cast est dangereux, mais dynamic_cast est plus lent…

Idée : utiliser une macro qui utilise dynamic_cast en mode debug et static_cast en mode release :

#ifndef CHECKCASTS_ENABLED# define CASTREFERENCE( TargetType, SourceType, expr ) \ (static_cast< TargetType& >( expr ))#else# define CASTREFERENCE( TargetType, SourceType, expr ) \ (DynamicCastReference< TargetType, SourceType >( expr, \ __FILE__, \ __LINE__, \ #expr ))#endif

Page 15: Page de garde

Des casts contrôlés en mode debug

Des casts contrôlés en mode debug

template< class TargetType, class SourceType > TargetType& DynamicCastReference( SourceType& expr, const char * const strFilename, const int iLineNumber, const char * const strExpr ){ if (&expr == NULL) return (TargetType &)expr; else { TargetType * const result= dynamic_cast< TargetType * >( &expr );

if (result == NULL) ReferenceCastFailed( strExpr, strFilename, iLineNumber, typeid( expr ).name(), typeid( TargetType ).name() ); return *result; }}

Page 16: Page de garde

Des casts contrôlés en mode debug

Des casts contrôlés en mode debug

Inconvénient : il faut écrire le type source de l’expression.

Mais, en cas d’erreur, l’exécution du programme se termine immédiatement (on n’attend pas que l’erreur se propage), avec un yoli message d’erreur indiquant :

le fichier et la ligne contenant l’erreur ;

l’expression fautive ;

son type dynamique ;

le type en lequel on a tenté de la convertir.

Et tout cela aisément débranchable en mode release.

Cela ne vaut-il pas la peine de taper quelques caractères supplémentaires ?

Page 17: Page de garde

L’opérateur instanceof en C++

L’opérateur instanceof en C++

Rappel : type_info::before n’apporte aucune indication hiérarchique !

tenter de caster avec dynamic_cast, en utilisant des pointeurs (donc en prenant l’adresse des références le cas échéant) et en testant que le résultat de dynamic_cast n’est pas 0/NULL, sauf si le pointeur l’était auparavant…

a instanceof A

((a == 0) || (dynamic_cast< A * >( a ) != 0))

Page 18: Page de garde

Utilisez au maximum le typage statique !

Utilisez au maximum le typage statique !

Une des forces de C++ pour la correction des programmes est son très fort typage statique.Ne vous adonnez pas aux "switches de type" : utilisez plutôt le polymorphisme !