Bonjour, je cherche un moyen propre de vérifier si mon objet est de tel ou tel type. typeid ne peut pas fonctionner car ces objets héritent du même.
Bonjour, je cherche un moyen propre de vérifier si mon objet est de tel ou tel type. typeid ne peut pas fonctionner car ces objets héritent du même.
Une solution : implémenter une fonction virtuelle GetTypeName() const qui renvoie le nome de la classe.
Avec une macro qui va bien, pour simplifier, ça peut donner ça :
A +
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 #define DECLARE_TYPE(Class) \ virtual std::string GetTypeName() const {return #Class; } //----------------------------------------------------------- // IObject : Base class //----------------------------------------------------------- struct IObject { IObject() {} virtual ~IObject() {} // Fonction to get the real type of the Class. virtual std::string GetTypeName () const = 0; // Polymorphic copy the object virtual boost::shared_ptr<IObject> Clone () const { throw std::exception("This object is no clonable"); } };
dynamic_cast<> devrait permettre ça (s'il s'agit juste de comparer deux types entre eux). C'est notamment le cas si les objets sont de la même hierachie.
RTTI doit être activé (ce n'est pas le cas dans les settings par défaut sous Visual Studio, mais c'est le cas avec g++).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 class A { }; class B : public A { }; class C : public A { }; class D : public B { }; A* a; ... D* d = dynamic_cast<D*>(a); // d == NULL si a n'est pas un objet du type D C* c = dynamic_cast<C*>(a); // c == NULL si c n'est pas un objet du type C
Ou es-ce que j'ao manqué quelque chose dans ta question ?
[FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.
Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.
Il demande un moyen propre ... "pourquoi faire ?" me parait être la meilleure réponse à sa question. S'il vient du Java il risque d'être habitué aux void*^WObject et non au typage bien plus strict des templates, plus les cas comme le double-dispatching qui peuvent-être résolu avec un pattern visiteur, ou encore le fait que le C++ dispose d'une meilleure sémantique de valeur ce qui lui permet d'esquiver les prises de choux que la communauté Java expérimente avec des fonctions comme equals (qui est un double dispatching dégénéré qui se résout avec les templates et 0 héritage en C++).
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
Bonjour,
type_id retourne le type dynamique de l'objet si le type statique est polymorphe :
Sortie :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 #include<typeinfo> #include <iostream> struct polymorphic_base { virtual ~polymorphic_base(){} }; struct polymorphic_derived : public polymorphic_base { }; struct non_polymorphic_base { ~non_polymorphic_base(){} }; struct non_polymorphic_derived : public non_polymorphic_base { }; template<class derivee, class base> void dump() { base b; derivee d; base &rb=b; base &rd=d; std::cout<<typeid(b).name()<<"\n"; std::cout<<typeid(d).name()<<"\n"; std::cout<<typeid(rb).name()<<"\n"; std::cout<<typeid(rd).name()<<"\n"; } int main() { std::cout<<"polymorphe : \n"; dump<polymorphic_derived,polymorphic_base>(); std::cout<<"non polymorphe : \n"; dump<non_polymorphic_derived,non_polymorphic_base>(); return 0; }
[EDIT] la réponse de Luc est la plus pertinente : pourquoi faire ?polymorphe :
struct polymorphic_base
struct polymorphic_derived
struct polymorphic_base
struct polymorphic_derived
non polymorphe :
struct non_polymorphic_base
struct non_polymorphic_derive
struct non_polymorphic_base
struct non_polymorphic_base
Ressources proposées par 3DArchi - Les fonctions virtuelles en C++ - Cours et tutoriels C++ - FAQ C++ - Forum C++.
je dois effectuer un traitement spécifique suivant l'objet. Si l'objet dérivé est de tel type je fais ceci, s'il est de se type, je fais cela...
La réponse de "poukill" me semble le plus adapter, a moins que j'ai mal compris le dynamic_cast<>
J'vais aller faire quelques testes.
Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.
Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.
En fait, si le seul moyen propre d'effectuer une action en fonction du type est de passer par une fonction virtuelle; la classe mère risque de se retrouver surchargée d'un tas de fonction.
Hors, certaines de ces fonctions n'auront pas lieu de se présenter en façade sur la classe. Je pense aux objets géographiques. D'accord, tu peux mettre "buffer" sur tous les objets dérivés de "Geometry".
Seulement tu ne peux pas mettre une opération trop rarement utilisée, du style un calcul d'indicateur "expérimental" que tu ne voudras pas forcément trainer tout au long de la vie de tes classes...
Dans ce cas, tu voudras extraire l'algorithme de ta classe et la mettre dans une classe d'algorithme.
double monIndicateur = ClasseAlgorithme::monIndicateur( objetGenerique );
Après, peut être que je suis trop pollué par les architectures java-like dans mon esprit...
Je ne vois pas trop quel lien tu fais entre fonctions virtuelles et fonctions "en facade" (qu'entends tu par là d'ailleurs ?). Si j'ai bien compris ce que tu veux dire, rien ne t'empêche de mettre cette fonction virtuelle en privé (comme la majorité des fonctions virtuelles).
Quand tu parles de toute la vie, c'est toute la vie pendant l'exécution du programme, ou toute la durée de vie du projet de développement ? (désolé si j'ai l'air bouché dans ma compréhension)Pourquoi pas, pour les algorithmes dont le code est le même pour toutes les classes de ta hiérarchies, ou alors si à l'appel de ton algorithme le type de l'objet sur lequel il est appelé est connu statiquement (c'est après tout comme ça que std::cout fait pour choisir le bon algorithme d'affichage de données).
Mais si le type exact n'est pas connu statiquement, et que l'algorithme, ou un bout de celui-ci, doit varier en fonction du type exact, je ne vois pas trop comment faire proprement sans recourir à un moment ou à un autre à une fonction virtuelle. Si tu ne fais pas ça, et qu'une nouvelle classe est ajoutée à ta hiérarchie, tu devras autrement modifier le code de tous ces algorithmes, et si tu en oublies un, tu auras un bug.
Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.
- Facade ( patron de conception ) : i.e. simplifier l'accès aux fonctionnalités en les posant directement sur la classe. L'abus de façade est dangereux pour l'évolution des codes ( tendance à former des classes disposant d'un nombre de fonctionnalités trop important )Je ne vois pas trop quel lien tu fais entre fonctions virtuelles et fonctions "en facade" (qu'entends tu par là d'ailleurs ?). Si j'ai bien compris ce que tu veux dire, rien ne t'empêche de mettre cette fonction virtuelle en privé (comme la majorité des fonctions virtuelles).
Ce qui me gène, c'est que hors le principe de l'héritage (au niveau des méthodes), il faut un mécanisme pour déterminer le type dynamiquement à l'exécution.
Je parle de la vie du projet de développement. Par exemple, lorsque des personnes se reposent sur tes classes de bases, si la seule alternative pour déterminée le type est l'héritage, ils seront contraints d'hériter de ta classe.Quand tu parles de toute la vie, c'est toute la vie pendant l'exécution du programme, ou toute la durée de vie du projet de développement ? (désolé si j'ai l'air bouché dans ma compréhension)
Je trouve dommage de ne pas pouvoir mettre en place des stratégies au niveau de l'exécution, sans utiliser le mécanisme de l'héritage. L'héritage créé un lien très fort avec la classe mère...Pourquoi pas, pour les algorithmes dont le code est le même pour toutes les classes de ta hiérarchies, ou alors si à l'appel de ton algorithme le type de l'objet sur lequel il est appelé est connu statiquement (c'est après tout comme ça que std::cout fait pour choisir le bon algorithme d'affichage de données).
De plus, parfois, on ne peut pas connaître le type au niveau de la compilation ( type connu statiquement? ). Je pense au cas des interpréteurs.
Si tu poses une méthode virtuelle getType() sur la classe mère et une énumération des types connus dans la même classe, tu peux procéder ainsi :Mais si le type exact n'est pas connu statiquement, et que l'algorithme, ou un bout de celui-ci, doit varier en fonction du type exact, je ne vois pas trop comment faire proprement sans recourir à un moment ou à un autre à une fonction virtuelle. Si tu ne fais pas ça, et qu'une nouvelle classe est ajoutée à ta hiérarchie, tu devras autrement modifier le code de tous ces algorithmes, et si tu en oublies un, tu auras un bug.
Pas de doute, il y a coût à cette généricité. Seulement, lorsque tu rajoutes le type D, ceci revient au même que si tu avais prévu une fonction virtuelle (non pure) te renvoyant une exception.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 if( objetGenerique.getType() == A::TYPE_A ){ //traitement suivant le type A }else if( objetGenerique.getType() == A::TYPE_B ){ //traitement suivant le type B }else if( objetGenerique.getType() == A::TYPE_C ){ //traitement suivant le type C }else{ /* mon choix de conception lorsque l'algorithme ne sait pas traiter un cas souvent une exception */ }
Il y a de gros avantage à cette méthode :
- tu peux extraire les algorithmes des classes
- un utilisateur de tes classes n'est pas obligé d'en hériter pour déterminer leur sous type
- tu peux utiliser ces classes dans un interpréteur ( au sens large : pour tous les éléments de mon fichier, j'applique mon algorithme )
Je trouve que le coût est minime face au gain. Qu'en penses tu?
Je pense personnellement que c'est plus de la POO oO ! (cf OCP).
The mark of the immature man is that he wants to die nobly for a cause, while the mark of the mature man is that he wants to live humbly for one.
--Wilhelm Stekel
Salut,
Je me demande si quelque chose basé sur le DP chaine de responsabilité ne serait pas ce que recherche le PO...
Mais, si les objets manipulés sont à ce point différents, j'aurais surtout tendance à penser à un problème de conception...
Ma première idée est que, d'une manière ou d'une autre, il essaye de faire cohabiter entre eux des types qui, bien qu'ayant un ancêtre commun (trop "générique") sont beaucoup trop différents pour y arriver (et je ne suis même pas loin de penser que cela vient de l'habitude de tout voir hériter de Object en java )
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
La fonction virtuelle non pure renvoyant une exception pour avoir un comportement par défaut, c'est une hérésie. Le NotImplementedException, ça ne devrait même pas exister (d'ailleurs, ça n'existe pas en c++).Pas de doute, il y a coût à cette généricité. Seulement, lorsque tu rajoutes le type D, ceci revient au même que si tu avais prévu une fonction virtuelle (non pure) te renvoyant une exception.
Il y a de gros avantage à cette méthode :
- tu peux extraire les algorithmes des classes
- un utilisateur de tes classes n'est pas obligé d'en hériter pour déterminer leur sous type
- tu peux utiliser ces classes dans un interpréteur ( au sens large : pour tous les éléments de mon fichier, j'applique mon algorithme )
Je trouve que le coût est minime face au gain. Qu'en penses tu?
La bonne manière, c'est que la virtuelle soit pure. Comme ça, l'erreur est à la compilation, si l'enfant ne définit pas la méthode. Et si elle n'a pas de sens pour cet objet, c'est probablement que la hiérarchie est mal conçue.
De plus, ça ne revient pas du tout au même. Avec ta méthode, quand on rajoute un type D, il faut modifier tous les algorithmes pour qu'ils fonctionnent avec D. Quel intérêt alors à l'héritage et la généricité qu'il est censé apporter ? Avec une fonction virtuelle, du moment qu'elle respecte le même contrat, tu as la garantie que ton programme ne va pas planter, et si tu respectes la même sémantique, que tes algos fonctionneront. Ça me un peu plus intéressant comme comportement. D peut être développé en indépendance totale des algos qui l'utilisent.
Pour les interpréteurs, je préfère généralement passer par des variants : pas de relation d'héritage, une liste connue de type. Si j'en rajoute un, il faut modifier partout, mais vu que j'ai pris soin de toujours tester avec des switch, et que la liste des types dans le variant soit un enum, alors le compilo va m'indiquer partout où je ne gère pas le cas rajouté. Chose qu'il n'est pas possible de faire avec un héritage.
Bonsoir,
Et C++/CLI alors? ( joke )...
white_tentacle, merci pour les remarques, j'en prend bonnes notes. J'avoue que je suis souvent trop retissant à l'idée de montée le degré de hiérarchie et parfois un peu laxiste.
Pour les interpréteurs, le gros avantage que j'avais vu était de ne pas repasser partout à l'ajout d'un type et de renvoyer une erreur d'exécution du style "La fonction <nom de la fonction> ne peut pas être appliquée sur un objet de la classe <nom de la classe>"...
Après tout, une erreur de type peut être une erreur du client :
Alors, afficher "Sqrt ne peut pas être appliquée sur String", ça peut très bien être le cas pour tout les types sauf "entier, flottant"... En gros, pas tant un NotImplementedException mais un BadInputTypeException.
Code : Sélectionner tout - Visualiser dans une fenêtre à part eval( sqrt( "toto" ) );
Après, j'avoue que ta méthode permet de faire la part entre les erreurs du programmeur et les erreurs d'exécution ... Je m'en inspirerai donc à l'avenir
Bonne soirée
L'exception c'est std::invalid_argument. Le truc, c'est que le message diffère pour chaque implémentation de chaque fonction normalement si on veux faire ça bien.
Comme c'est répétitif, je te conseil un coup de méta-prog dessus.
Un truc qui pourrait être sympa, c'est de faire des foncteurs pour commande, avec un petit factory-like derrière... Après, je ne m'y connais pas en interpréteur, c'est juste une idée comme ça -_-' !
The mark of the immature man is that he wants to die nobly for a cause, while the mark of the mature man is that he wants to live humbly for one.
--Wilhelm Stekel
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager