Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Problèmes d'upcast

5 réponses
Avatar
No_Name
Bonjour,

J'ai une classe CB virtuelle de base, dont dérivent les classes CS, CT,
CQ, CM.

J'ai aussi une classe virtuelle de base CC.

J'ai des classes CSC, CTC, CQC, CMC qui dérivent respectivement des
classes CS, CT, CQ, CM et aussi de la classe CC (par héritage
multiple).

La hiérarchie est donc :

CB <-- CS
CB <-- CT
CB <-- CQ
CB <-- CM

CS<--!
!-- CSC
CC<--!

CT<--!
!-- CTC
CC<--!

CQ<--!
!-- CQC
CC<--!

CM<--!
!-- CMC
CC<--!

Je récupère un pointeur vers un objet quelconque dans un pointeur de
type C. J'ignore quel est le vrai type de l'objet récupéré (CM ou CMC,
CT ou CTC, CQ ou CQC etc.)

a) Y a t-il un moyen simple de connaitre le type de mon objet ?

b) Y a t-il un moyen simple de savoir si mon objet dérive de la classe
CC ou pas (même sans connaitre son vrai type) ?

c) Y a t-il un moyen simple et générique de faire un upcast, c'est à
dire un cast de mon objet vers sa classe mère (CTC vers CT, CQC vers CQ
...)

Pour le moment, les seuls solutions que je vois sont de tester le type
de l'objet avec des casts successifs, du genre :

if(CQC* a = dynamic_cast<CQC*>(mon_objet))
{
// mon_objet est de type CQC
}

mais je ne suis pas sur que cela soit vraiment une solution
satisfaisante ... Peut-on faire plus simple ?

Merci de votre aide.

5 réponses

Avatar
No_Name
>
Je récupère un pointeur vers un objet quelconque dans un pointeur de type C.



Petite erreur : je voulais dire :

Je récupère un pointeur vers un objet quelconque dans un pointeur de
type CB.
Avatar
James Kanze
On Feb 17, 2:03 pm, No_Name wrote:

J'ai une classe CB virtuelle de base, dont dérivent les
classes CS, CT, CQ, CM.



J'ai aussi une classe virtuelle de base CC.



J'ai des classes CSC, CTC, CQC, CMC qui dérivent
respectivement des classes CS, CT, CQ, CM et aussi de la
classe CC (par héritage multiple).



La hiérarchie est donc :



CB <-- CS
CB <-- CT
CB <-- CQ
CB <-- CM



CS<--!
!-- CSC
CC<--!



CT<--!
!-- CTC
CC<--!



CQ<--!
!-- CQC
CC<--!



CM<--!
!-- CMC
CC<--!



Je récupère un pointeur vers un objet quelconque dans un
pointeur de type C.



De type CB, selon ton follow-up.

J'ignore quel est le vrai type de l'objet récupéré (CM ou CMC,
CT ou CTC, CQ ou CQC etc.)



a) Y a t-il un moyen simple de connaitre le type de mon objet ?



Plus ou moins. Il y a typeid, ce qui te donne un std::type_info.
Avec trois fonctionnalités éventuellement intéressantes :

-- On peut comparer deux type_info pour l'égalité. S'ils
comparent égaux (== renvoie true), ils représentent le même
type, sinon, ils représentent des types différents.

-- Il a une fonction name(), qui renvoie le nom du type. Dans
un format défini par l'implémentation, et qui varie d'un
compilateur à l'autre, mais souvent, c'est assez utilisable.
(L'exception ici est g++, qui renvoie le nom manglé.)

-- Il y a une fonction before(), qui établit un ordre strict
(mais arbitraire) sur les types, et qui peut donc servir
dans des std::map ou des vector triés.

En fin de compte, la question est pourquoi tu veux cette
fonctionnalité. Ou plutôt ce que tu entends par « connaître le
type » ; en C++, le type est fondamentalement un concept du
compilateur, qui n'existe que d'une façon limité lors de
l'exécution. Si c'est pour savoir si deux objets ont le même
type, c'est trivial :

bool
ontMemeType( CB* p1, CB* p2 )
{
return typeid( *p1 ) == typeid( *p2 ) ;
}

Pour d'autres choses, ça peut être plus ou moins compliquées ;
pour afficher le type pour des motifs de déboggage,
typeid(*p).name() fera en général l'affaire, mais ça ne vaut
plus si le but c'est la sérialisation, ou d'autres applications
où il faut une identité précise.

b) Y a t-il un moyen simple de savoir si mon objet dérive de
la classe CC ou pas (même sans connaitre son vrai type) ?



CC*
asCC( CB* p )
{
return dynamic_cast< CC* >( p ) ;
}

renvoie un pointeur à la base CC, si elle existe, sinon un
pointeur null.

c) Y a t-il un moyen simple et générique de faire un upcast,
c'est à dire un cast de mon objet vers sa classe mère (CTC
vers CT, CQC vers CQ ...)



Si tu as un CTC*, il se convertit implicitement en CT*, sans
problème. Si tu a un CB* (qui pointe à un CTC, ou à d'autre
chose), dynamic_cast< CT* >( p ) renvoie un pointeur au CT, s'il
existe (et n'est pas ambigu, mais il n'y a pas de possibilités
d'ambiguïtés dans ton hièrarchie), et null sinon.

Pour le moment, les seuls solutions que je vois sont de tester
le type de l'objet avec des casts successifs, du genre :



if(CQC* a = dynamic_cast<CQC*>(mon_objet))
{
// mon_objet est de type CQC
}



mais je ne suis pas sur que cela soit vraiment une solution
satisfaisante ... Peut-on faire plus simple ?



Ça dépend de ce dont tu as réelement besoin. Qu'est-ce que tu
essaie de faire, au juste.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
No_Name
>
Ça dépend de ce dont tu as réelement besoin. Qu'est-ce que tu
essaie de faire, au juste.



Merci pour vos réponses.

Je gère une interface de saisie, et les différentes classes CT, CC, CQ,
CM et CTC, CQC, CMC ... correspondent aux différents états de l'objet
choisi selon les options sélectionnées par l'utilisateur.

En gros, si l'objet courant est un objet CM et que le client choisi
l'option C, on transforme l'objet CM en objet CMC.
Si il se ravise et désélectionne l'option C, on repasse d'un objet CMC
à un objet CM etc.
Avatar
James Kanze
On Feb 18, 10:14 am, No_Name wrote:
> Ça dépend de ce dont tu as réelement besoin. Qu'est-ce que tu
> essaie de faire, au juste.



Je gère une interface de saisie, et les différentes classes
CT, CC, CQ, CM et CTC, CQC, CMC ... correspondent aux
différents états de l'objet choisi selon les options
sélectionnées par l'utilisateur.



En gros, si l'objet courant est un objet CM et que le client
choisi l'option C, on transforme l'objet CM en objet CMC. Si
il se ravise et désélectionne l'option C, on repasse d'un
objet CMC à un objet CM etc.



C-à-d qu'on crée un nouvel objet chaque fois, qui remplace
l'ancien, et que le type de ce nouvel objet dépend du type de
l'ancien (et l'action de l'utilisateur). La seule solution qui
me vient tout de suite à l'esprit, c'est des factories pour la
création des objets, et des maps pour chaque action, qui mappe
l'ancien type vers la factory du nouveau. Il te faut donc un
wrapper des type_info, pour servir de clé :

class Type
{
public:
Type(
std::type_info const&
type )
: myData( &type )
{
}
bool operator<(
Type const& rhs ) const
{
return myData->before( rhs.myData ) ;
}

private
std::type_info const*
myData ;
} ;

(La prochaine version de la norme en fournira une, sous le nom
std::type_index. Mais jusqu'alors...)

Tu as aussi des factory, selon le cas des classes qui dérive
d'une classe abstraite, ou simplement des fonctions, du genre :

CB*
createCTCfromCT( CB const* old )
{
CT* tmp( dynamic_cast< CT* >( old ) ) ;
assert( tmp != NULL ) ;
return new CTC( *old ) ;
}

En fait, tu dois pouvoir te servir d'un template pour générer
ces fonctions (ou des classes dérivées de l'interface).

(En fait, je préfèrerais probablement la solution avec des
classes, avec une instance statique de chaque classe dérivée.
D'une part, le constructeur de la classe pourrait insérer
l'instance automatiquement dans le map, ce qui simplifie
l'initialisation, et de l'autre, la classe pourrait contenir des
fonctions virtuelles différentes pour chaque évenement, ce qui
fait qu'on n'aurait besoin que d'un seul map. Mais beaucoup de
variations sont possibles -- on pourrait utiliser un seul map
avec comme clé un struct avec Type et l'identificateur de
l'évenement, par exemple.)

Pour chaque évenement, alors, un
std::map< Type, CB* (*)( CB const* ) > factoryMap ;
convenablement rempli, et à l'évenement :
return (*factoryMap[ typeid( *old ) ])( old ) ;

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
No_Name
James Kanze a écrit :
On Feb 18, 10:14 am, No_Name wrote:
Ça dépend de ce dont tu as réelement besoin. Qu'est-ce que tu
essaie de faire, au juste.





Je gère une interface de saisie, et les différentes classes
CT, CC, CQ, CM et CTC, CQC, CMC ... correspondent aux
différents états de l'objet choisi selon les options
sélectionnées par l'utilisateur.



En gros, si l'objet courant est un objet CM et que le client
choisi l'option C, on transforme l'objet CM en objet CMC. Si
il se ravise et désélectionne l'option C, on repasse d'un
objet CMC à un objet CM etc.



C-à-d qu'on crée un nouvel objet chaque fois, qui remplace
l'ancien, et que le type de ce nouvel objet dépend du type de
l'ancien (et l'action de l'utilisateur). La seule solution qui
me vient tout de suite à l'esprit, c'est des factories pour la
création des objets, et des maps pour chaque action, qui mappe
l'ancien type vers la factory du nouveau.



J'ai en effet créé une Factory avec trois méthodes statiques qui
permettent de créer l'objet dont j'ai besoin (CreateObject()), de faire
un Upcast (UpCast)) ou de faire un DownCast (DownCast()).

Par contre, je ne suis pas familier des maps, mais à la lueur de ta
suggestion, je vais m'y intéresser de près. Merci.