Polymorphisme et static_cast

Polymorphisme et static_cast - C++ - Programmation

Marsh Posté le 21-08-2013 à 17:36:47    

Hello à tous,
Je m'y perds un peu dans tous ces principes de cast et de polymorphisme alors j'en appel à vous.

 

Soit le code suivant :

Code :
  1. class I
  2. {
  3. public:
  4. I() {};
  5. virtual ~I() {};
  6. virtual void i() = 0;
  7. };
  8. class Base: public I
  9. {
  10. public:
  11. int foo;
  12. void i() { foo = 42; }
  13. };
  14. class Fille: public Base
  15. {
  16. public:
  17. int bar;
  18. };
  19. int main()
  20. {
  21. I *pI = new Base();
  22. Fille *pFilleStatic = static_cast<Fille *>(pI);
  23. Fille *pFilleDynamic = dynamic_cast<Fille *>(pI);
  24. }
 

Question 1 :
Est ce légal de faire ça ?

 

Question 2 :
Le static_cast semble fonctionner mais le dynamic_cast me retourne un pointeur nul.
Pourquoi ?

 

Merci pour votre aide  :jap:


Message édité par azubal le 21-08-2013 à 17:38:05
Reply

Marsh Posté le 21-08-2013 à 17:36:47   

Reply

Marsh Posté le 21-08-2013 à 17:51:22    

Non, c'est illégal de faire un static cast dans ton cas.
dynamic cast te retourne nullptr parce que l'instance que tu manipules n'est pas de la classe dans laquelle tu convertis.

 

Edit : si tu faisais I *pI = new Fille(); tout le reste serait légal et fonctionnerait.


Message édité par theshockwave le 21-08-2013 à 17:52:05

---------------
last.fm
Reply

Marsh Posté le 21-08-2013 à 17:59:30    

Arf!
Je me doute bien mais pour garder une certaine modularité je ne voudrai pas toucher à "I *pI = new Base();".
 
Comment faire (proprement) pour convertir ça vers le type de mon choix (du moment qu'il hérite de Base) ?
 
 
Edit: Et si je bricole une factory à base de template pour specifier le type que je veux creer avec new ?

Code :
  1. class Factory
  2. {
  3. template<T=Base> static T createObject() { return new T(); }
  4. };


 
Et que dans mon main je puisse faire

Code :
  1. I *pI = Factory::createObject<Fille>(); ?


Message édité par azubal le 21-08-2013 à 18:24:50
Reply

Marsh Posté le 21-08-2013 à 18:30:03    

tu ne peux pas ...
Si la classe instanciée est au-dessus de toi, tu ne peux pas magiquement la convertir dans un type qui en dérive. Fin de l'histoire.

 

Si tu ne peux pas changer le type instancié, c'est perdu ... Mais je doute que ce soit vraiment le cas, non ? Pourquoi est-ce que tu te refuses à instancier directement une classe fille ?
j'aurais du lire avec le doigt.
oui tu peux faire ca, et c'est effectivement un pattern classique.
En général, tu vas donner une forme d'identifiant pour décrire la classe à instancier.
Chaque classe devra avoir été au préalable enregistrée dans ta factory


Message édité par theshockwave le 21-08-2013 à 18:33:35

---------------
last.fm
Reply

Marsh Posté le 21-08-2013 à 18:50:09    

En réalité j'utilise une class "Server" avec un membre "Client *createClient()" qui me sort un pointeur vers un objet Client à chaque fois que je l'appelle.
Ce membre ne fait rien d'autre que faire un "new Client(...)" en passant les bons parametres et en settant les différents champs de Client (qui à Server en "friend" ).

 

Maintenant je voudrai surcharger Client pour lui ajouter de nouvelles fonctionnalités mais continuer à faire en sorte que ce soit "Server" qui me crée les objets. En revanche "Server" ne peut pas avoir connaissance de ma classe hérité "SuperClient".


Message édité par azubal le 21-08-2013 à 18:51:23
Reply

Marsh Posté le 21-08-2013 à 19:35:24    

Il doit y avoir des millions d'exemples pour des factory ...
 

Code :
  1. #include <string>
  2. #include <map>
  3. #include <iostream>
  4. using namespace std;
  5. struct IClient
  6. {
  7.   virtual ~IClient() {}
  8.   virtual void Print()
  9.   {
  10.     cout << "IClient::Print()" << endl;
  11.   }
  12. };
  13. struct IClientParams{};
  14. struct Server
  15. {
  16.   typedef IClient* (*ClientConstructor)( const IClientParams& params );
  17.   typedef map< string, ClientConstructor > ClientCtorMap;
  18.   static void Register( const string& clientName, ClientConstructor ctor )
  19.   {
  20.     staticClientCtorMap[ clientName ] = ctor;
  21.   }
  22.   IClient* BuildClient( const string& clientName, const IClientParams& params )
  23.   {
  24.     ClientCtorMap::const_iterator clientCtorIt = staticClientCtorMap.find( clientName );
  25.     if( clientCtorIt != staticClientCtorMap.end() )
  26.     {
  27.       return (*clientCtorIt->second)( params );
  28.     }
  29.     return NULL;
  30.   }
  31.   static ClientCtorMap staticClientCtorMap;
  32. };
  33. Server::ClientCtorMap Server::staticClientCtorMap;
  34. struct ClientBlah : IClient
  35. {
  36.   static IClient* Build( const IClientParams& params )
  37.   {
  38.     return new ClientBlah( params );
  39.   }
  40.  
  41.   ClientBlah( const IClientParams& params )
  42.     : params( params )
  43.   {
  44.   }
  45.   virtual void Print()
  46.   {
  47.     cout << "ClientBlah::Print()" << endl;
  48.   }
  49.  
  50.   IClientParams params;
  51. };
  52. int main()
  53. {
  54.   Server::Register( "ClientBlah", &ClientBlah::Build );
  55.  
  56.   Server server;
  57.   IClient* pClient = server.BuildClient( "ClientBlah", IClientParams() );
  58.   pClient->Print();
  59.   delete pClient;
  60.   return 0;
  61. }


 

Code :
  1. s@FRWP1S ~
  2. $ g++ -o plop.exe -Wall plop.cpp
  3. s@FRWP1S ~
  4. $ ./plop.exe
  5. ClientBlah::Print()


 
Edit : fix pour l'indentation cassée


Message édité par theshockwave le 21-08-2013 à 19:36:27

---------------
last.fm
Reply

Marsh Posté le 22-08-2013 à 19:19:31    

Merki.

 

Finalement je m'en suis sorti avec mes templates :

 


Code :
  1. class Server
  2. {
  3.   template <class T>
  4.   T *createClient()
  5.   {
  6.     T *pT = new T();
  7.     pT->foo = 42;
  8.     return pT;
  9.   }
  10. }
  11. class Client
  12. {
  13. public:
  14.   int foo;
  15. };
  16. class SuperClient : public Client
  17. {
  18. };
  19. int main()
  20. {
  21.   Server server;
  22.   SuperClient *pClient = server.createClient<SuperClient>();
  23. }
 

En revanche je me demande si ya moyen de spécifier que la class passé en template doit forcement dériver de "Client" ?

Message cité 1 fois
Message édité par azubal le 22-08-2013 à 19:20:58
Reply

Marsh Posté le 22-08-2013 à 19:35:54    

azubal a écrit :

Merki.
 
Finalement je m'en suis sorti avec mes templates :
 
 

Code :
  1. class Server
  2. {
  3.   template <class T>
  4.   T *createClient()
  5.   {
  6.     T *pT = new T();
  7.     pT->foo = 42;
  8.     return pT;
  9.   }
  10. }
  11. class Client
  12. {
  13. public:
  14.   int foo;
  15. };
  16. class SuperClient : public Client
  17. {
  18. };
  19. int main()
  20. {
  21.   Server server;
  22.   SuperClient *pClient = server.createClient<SuperClient>();
  23. }


 
En revanche je me demande si ya moyen de spécifier que la class passé en template doit forcement dériver de "Client" ?


 
Pas de manière élégante. Les "concepts" ont été repoussés au prochain standard au moins.
Cela étant dit, tu peux probablement tricher en faisant un static_cast<IClient*>( pT ); qui causera une erreur de compilation si le type n'est pas de la bonne famille.


---------------
last.fm
Reply

Marsh Posté le 23-08-2013 à 18:55:58    

ou utiliser boost::is_base_derived

Reply

Marsh Posté le 25-08-2013 à 00:34:04    

Joel F a écrit :

ou utiliser boost::is_base_derived


 
Ca fait le test au runtime ou à la compilation, ca ?
Parce que dans son cas, le top, c'est d'avoir le message à la compilation ...


---------------
last.fm
Reply

Marsh Posté le 25-08-2013 à 00:34:04   

Reply

Marsh Posté le 25-08-2013 à 10:52:35    

Ce serait mieux à la compilation en effet.
Et je fais partie de ceux qui (très probablement à tord) ne veulent pas utiliser boost.

Reply

Marsh Posté le 26-08-2013 à 14:51:20    

c'est a la compilation.
 
Apres l'outil est la si vous voulez vous cassez les pieds a reinventer la roue carre, allez y.

Reply

Marsh Posté le 26-08-2013 à 16:13:34    

Joel F a écrit :

c'est a la compilation.
 
Apres l'outil est la si vous voulez vous cassez les pieds a reinventer la roue carre, allez y.


 

Citation :

This version detects ambiguous base classes and private base classes correctly


 
Cette info en début de la doc me fait me demander si c'est toujours désirable de connaître l'héritage privé des classes.
 
 
Et accessoirement, parfois, on a des contraintes qui font aussi que non, on ne peut pas s'en servir, parce que "l'outil" ne fait pas partie du standard et parce qu'on a des restrictions spécifiques
(Et personnellement, je suis dans le cas bâtard où une partie du projet sur lequel je bosse a droit à C++11 et boost, mais pas le reste du projet [:petrus75] )
Du coup, ca reste bien de savoir faire une roue octogonale  [:petrus75]  


---------------
last.fm
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed