Problème de cast ou de design

Version imprimable

Je vais me documenter à ce sujet plutôt.
Citation:

Envoyé par poukill Voir le message
Iil y a déjà eu pas mal de question là dessus, une petite recherche sur le forum avec le mot clé downcasting devrait de donner pas mal d'infos (je me souviens avoir posé moi-même des questions là dessus il fut un temps).

Ok, merci beaucoup.
  • 20/02/2013, 15h31
    poukill
    Citation:

    Envoyé par LinuxUser Voir le message
    Je me suis pas encore penché sur les templates, peut être est-ce le moment?

    Attention, le template method est un pattern, le nom est trompeur... Rien à avoir avec les template C++.
  • 20/02/2013, 15h35
    jblecanard
    Citation:

    Envoyé par LinuxUser Voir le message
    Je me suis pas encore penché sur les templates,

    Je vote pour le visiteur comme te l'as conseillé Poukill, c'est un pattern utile à connaître.
  • 20/02/2013, 16h24
    Bousk
    Citation:

    Envoyé par LinuxUser Voir le message
    Je vois pas trop l'intérêt, j'ai l'impression que le problème est juste déplacé.

    L'intérêt c'est de passer non pas un Mere& mais un Trucoidal& en paramètre.
    L'héritage revient à factoriser des comportements et services. Devoir faire appel à une méthode spécifique à un type fille n'est pas du tout normal et montre au moins un gros manque architectural.
    Si ta méthode doit faire appel à une méthode de fille
    - elle doit prendre un fille en paramètre
    --> sinon elle traite comment les cas où c'est pas une fille mais fille2 ?!?
    - la méthode de fille n'est pas spécifique à fille mais spécialisée
    --> on factorise alors le comportement et service à travers cette méthode
  • 20/02/2013, 16h45
    r0d
    En fait, le problème qui se pose ici est assez courant, et il vient du fait que le protocole (ensemble des fonctions et variables publiques de la classe) de la classe mère est différent de celui de la classe fille. Ça viole le LSP, et donc, ça pose potentiellement tout un tas de problèmes (celui évoqué ici n'en est qu'un parmi bien d'autres).

    Donc effectivement, comme toujours, la solution consiste à ajouter un niveau d'indirection. Visiteur est généralement approprié.

    Parfois il n'est pas nécessaire de respecter LSP, mais c'est assez rare. Il s'agit de cas où on est sûr que le code qui utilise ces classes ne déclarera jamais un objet de type Mere, et donc qu'il n'utilisera que les classes héritées. Mais en dehors de ce cas rare, respecter LSP permet un confort non négligeable dans le cycle de développement. Confort dont on ne prend pas forcément conscience, puisque les problèmes ne posent pas... mais je m'égare...
  • 20/02/2013, 20h06
    LinuxUser
    Dans mon cas, je ne cherche pas à respecter LSP, car les classe Filles sont des implémentations différentes de la classe mère (qui n'est jamais instanciée et ne le sera jamais).

    En fait, mon idée c'est plutôt:
    * j'ai une classe mère abstraite : Mere
    * j'ai des classes filles qui implémentent Mere: Fille1, Fille2, Fille3
    * j'ai des fonctions qui utilisent Fille1 ou Fille2 ou Fille3 quasiment de la même manière, et donc pour factoriser je fait:
    Code:

    1
    2
    3
    void function(Mere& m)
    {...// code polymorphe qui fonctionne que ce soit Fille1, 2 ou 3
    }

    Sauf que dans cette fonction je voudrait faire un seul appel de méthode spécifique à Fille2 par exemple:
    Code:

    1
    2
    3
    4
    void function(Mere& m)
    {...// tout le code idem que précédemment
    m.methodeDeFille2();
    }

    Je trouverais dommage de devoir dupliquer du code à cause d'une seule méthode:
    Code:

    1
    2
    3
    void function(Fille1& f);
    void function(Fille2& f);
    void function(Fille3& f);

    Et donc avoir 3 fonctions avec 99% de code identique.
  • 20/02/2013, 20h41
    Bousk
    Ce que je trouve dommage c'est de faire de l'héritage pour quelque chose qui n'en est clairement pas. :cry:
    Et vouloir factoriser des lignes de code, bien que je sois le premier à dire que les programmeurs sont fénéants, est une très mauvaise idée.
  • 20/02/2013, 21h21
    LinuxUser
    Citation:

    Envoyé par Bousk Voir le message
    Ce que je trouve dommage c'est de faire de l'héritage pour quelque chose qui n'en est clairement pas. :cry:

    Même si ce n'est pas LSP, les classes filles partagent certaines caractéristiques, donc l'héritage ne me semble pas absurde de ce cas.
    Citation:

    Envoyé par Bousk Voir le message
    Et vouloir factoriser des lignes de code, bien que je sois le premier à dire que les programmeurs sont fénéants, est une très mauvaise idée.

    Je devrais dupliquer les méthodes pour chaque implémentations?
  • 20/02/2013, 21h31
    Flob90
    Bonsoir,

    J'ai l'impression que la discussion tourne un peu dans le vide. Si arriver à ce problème est classique, les moyens de le résoudre sont nombreuses et dépendent énormément de l'objectif du code.

    Tu pourrais concrétiser un peu le contexte de ton code ? Ça serait plus simple pour t'indiquer une solution réellement adaptée. Idéalement avec un mini exemple de la façon dont doit être utilisé le code (ça donne de nombreuse indications dans les choix techniques).
  • 21/02/2013, 11h45
    Médinoc
    Si Mere est une classe abstraite, pourquoi ne pas tout simplement lui donner également une méthode abstraite?

    Code:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    struct Mere {
    	virtual void TraitementPourFonction() = 0;
    };
    struct Fille1 : public Mere {
    	void TraitementPourFonction();
    };
    struct Fille2 : public Mere {
    	void TraitementPourFonction();
    };
     
     
    void function(Mere& m)
    {
    	//Code pour les 3
    	//...
    	m.TraitementPourFonction();
    	//Code pour les 3
    	//...
    }

  • 21/02/2013, 15h10
    koala01
    Salut,
    Citation:

    Envoyé par LinuxUser Voir le message
    Dans mon cas, je ne cherche pas à respecter LSP, car les classe Filles sont des implémentations différentes de la classe mère (qui n'est jamais instanciée et ne le sera jamais).

    C'est, en gros, ce que les piliers de la programmation orientée objets conseillent de manière très appuyée avec le I de SOLID (qui est mis pour ISP: Interface Segregation Principle) ;)

    Mais cela ne veut en aucun cas dire qu'il faille accepter de ne pas respecter LSP pour la cause :aie:

    Que ta classe de base soit une classe "tout ce qu'il y a de plus classique", une classe abstraite ou même qu'elle ne présente qu'un partie d'interface, dés le moment où tu envisages l'héritage, le LSP te donne un GO/ NO GO qui t'indique si, oui ou non, tu peux effectivement y recourir.

    Si tu acceptes de ne pas respecter ce principe de base, tu peux te dire que tu dénature totalement la philosophie même de la programmation orientée objets parce que tu acceptes l'idée de ne pas t'intéresser à la substituabilité de tes classes :aie:

    Bref, s'il est vrai que l'intérêt d'un respect stricte de LSP ne saute pas forcément aux yeux au début, il faut être conscient du fait que son non respect se paye cash très rapidement ;)
    Citation:

    En fait, mon idée c'est plutôt:
    * j'ai une classe mère abstraite : Mere
    * j'ai des classes filles qui implémentent Mere: Fille1, Fille2, Fille3
    * j'ai des fonctions qui utilisent Fille1 ou Fille2 ou Fille3 quasiment de la même manière, et donc pour factoriser je fait:
    Code:

    1
    2
    3
    void function(Mere& m)
    {...// code polymorphe qui fonctionne que ce soit Fille1, 2 ou 3
    }


    C'est ce à quoi servent les fonctions virtuelles et leur pendant: les comportements polymorphes ;)
    Citation:

    Sauf que dans cette fonction je voudrait faire un seul appel de méthode spécifique à Fille2 par exemple:
    Code:

    1
    2
    3
    4
    void function(Mere& m)
    {...// tout le code idem que précédemment
    m.methodeDeFille2();
    }

    Je trouverais dommage de devoir dupliquer du code à cause d'une seule méthode:
    Code:

    1
    2
    3
    void function(Fille1& f);
    void function(Fille2& f);
    void function(Fille3& f);

    Et donc avoir 3 fonctions avec 99% de code identique.
    Sauf que, si tu envisages la possibilité de devoir appeler une fonction particulière de Fille2 dans une situation particulière, tu peux partir du principe sans risquer de te tromper que, tôt ou tard, tu voudras aussi pouvoir utiliser une fonction particulière de Fille3 ou de Fille5 (qui est encore à créer :D) dans certaines circonstances particulières, auxquelles tu ne penseras qu'au moment où tu envisageras des évolutions.

    La question n'est pas ici de savoir SI cela arrivera, car je peux t'assurer que tu y viendras, mais bien de savoir QUAND cela arrivera ;)


    De plus, l'idée n'est pas de dupliquer du code, mais de déléguer les responsabilités afin, justement, d'éviter les duplications de code inutiles ;)

    Or, si tu veux éviter d'avoir sans arrêt à vérifier le type réel de la variable pointée par un pointeur "passant pour être" du type de base afin de savoir comment la manipuler, la meilleure solution passe par le double dispatch.

    Il s'agit de se baser sur le principe que la variable qui est pointée par un pointeur "passant pour être du type de base" connait parfaitement son type réel et que c'est ce type réel qui sera transmis en paramètre si on décide d'appeler n'importe quelle fonction en passant this (ou *this).

    L'idée est donc de prévoir un comportement polymorphe (autrement dit, une fonction virtuelle (pure si besoin) ;)) dans la classe de base dont le comportement adapté dans les classes dérivées aura pour résultat d'appeler une fonction particulière prenant une référence (ou un pointeur si besoin) sur un objet du type réel.

    Le pattern visiteur est une des manières régulièrement utilisées pour mettre le double dispatch en oeuvre ;)
    Citation:

    Envoyé par LinuxUser Voir le message
    Même si ce n'est pas LSP, les classes filles partagent certaines caractéristiques, donc l'héritage ne me semble pas absurde de ce cas.

    Je devrais dupliquer les méthodes pour chaque implémentations?

    NON!!!

    Soit LSP est respecté et l'héritage est cohérent, soit LSP n'est pas respecté et tu NE PEUX PAS envisager l'héritage.

    Il faut bien se rappeler ici que LSP est un principe de conception, c'est à dire qu'il s'agit d'un principe qui doit être utilisé pour décider s'il est opportun ou non de recourir à l'héritage.

    Soit LSP est respecté, et l'on peut alors envisager de recourir à l'héritage sans problème (ce qui ne veut pas du tout dire qu'il faudra d'office y recourir ;)), soit LSP n'est pas respecté, et, dans ce cas, il ne faut absolument pas recourir à l'héritage ;)

    Il s'agit donc, comme je l'ai indiqué plus haut d'un GO / NO GO: On peut le faire (sans y voir la moindre obligation cependant) si on a le GO, on ne peut pas le faire (sans y voir la moindre exception) si on a le NO GO ;)
  • 21/02/2013, 15h38
    white_tentacle
    Citation:

    Envoyé par koala01 Voir le message
    Soit LSP est respecté et l'héritage est cohérent, soit LSP n'est pas respecté et tu NE PEUX PAS envisager l'héritage.

    J’appuie tout ce qu’a dit Koala, et me permets de suggérer un héritage privé / protégé dans le cas présent. Si tu hérites afin de factoriser du code, mais que tu ne souhaites pas de polymorphisme, c’est une solution envisageable.
  • 21/02/2013, 15h49
    LinuxUser
    OK, donc je viens de comprendre que je n'aurais pas dû utiliser l'héritage dans mon cas.

    Mais justement, dans mon cas où les classes ont un comportement très proches (à quelques exeptions près), la seule solution est le pattern visiteur?

    Et donc si je recommencais le design des classes (sans les faire hériter d'une classe mère), je les traiterai indépendemment sans rapports les unes avec les autres?
  • 21/02/2013, 15h57
    Flob90
    Tu ne veux vraiment pas détailler un peu ton problème de manière concrète ?

    Parce que personnellement quand tu dis que tu as factorisé des comportements dans des classes mères qui ne seront jamais instancié seules, je pense aux classes de politique (qui sont bien réalisées via des héritages publique en général, mais avec une nuance au niveau du destructeur).
  • 21/02/2013, 16h05
    LinuxUser
    OK, je rédige proprement et je post.
  • 21/02/2013, 17h10
    koala01
    Pour ce que j'entrevois, dans la brume de ton imprécision actuelle, de ton problème, je dirais que la solution passe, décidément, par une délégation correcte des responsabilités.

    Quitte à "tout recommencer", je te conseillerais vivement de veiller en permanence à ce que chaque classe n'ait jamais qu'une et une seule responsabilité clairement définie (si possible autre que de "gérer les machins" :D)

    De cette manière, si deux classes distinctes (et n'ayant aucun lien entre elles) doivent avoir un comportement plus ou moins similaire, il te sera facile d'utiliser, justement, une troisième classe dont la responsabilité serait de présenter le comportement en question ;)
  • 21/02/2013, 17h52
    LinuxUser
    Je vais essayer d'être clair.
    J'ai créé un exemple bidon pour reproduire mon problème, avec une classe mère "Methode", deux filles "Simple" et "Complexe", une classe "MethodeUser" qui utilise les méthodes et une classe "User".
    Je répète que les fonctions et calculs n'ont pas de sens, et servent juste pour illustrer.

    Classe Methode:
    Code:

    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
    #ifndef METHODE_H_
    #define METHODE_H_
     
    #include <vector>
     
    class Methode
    {
    public:
    /******************************** CONSTRUCTOR *********************************/
    	/**
             * @brief Default constructor.
             */
    	Methode();
    /********************************* DESTRUCTOR *********************************/
    	/**
             * @brief Default destructor.
             */
    	virtual ~Methode();
    /*********************************** METHODS **********************************/
    	virtual double calculation(double& a, double& b) const=0;
    	virtual function1(double& a) = 0;
    	void printInputsValues() const;
    	void printSolutionValue() const;
     
    protected:
    	double m_solution;
    	std::vector<double> m_inputs;
     
    };
     
    #endif /* METHODE_H_ */

    Classe Simple:
    Code:

    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
    #ifndef SIMPLE_H_
    #define SIMPLE_H_
     
    #include "Methode.h"
     
    class Simple: public Methode
    {
    public:
    /******************************** CONSTRUCTOR *********************************/
    	/**
             * @brief Default constructor.
             */
    	Simple();
    /********************************* DESTRUCTOR *********************************/
    	/**
             * @brief Default destructor.
             */
    	virtual ~Simple();
    /*********************************** METHODS **********************************/
    	virtual double calculation(double& a, double& b);
    	virtual void function1(double& a);
    /*********************************** GETTERS **********************************/
    private:
    	double m_coeff;
     
    };
     
    #endif /* SIMPLE_H_ */

    Code:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #include "Simple.h"
     
    double Simple::calculation(double a, double b)
    {
      double res = 0.0;
     
      function1(a);
     
      res = a + m_inputs[b];
     
      return (res);
    }
     
    void Simple::function1(double& a)
    {
      int i=0;
      for(i=0;i<m_inputs.size();++i)
      {
        m_inputs[i] = m_coeff*a;
      }
    }

    Classe Complexe
    Code:

    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
    #ifndef COMPLEXE_H_
    #define COMPLEXE_H_
     
    #include "Methode.h"
     
    class Complexe: public Methode
    {
    public:
    /******************************** CONSTRUCTOR *********************************/
    	/**
             * @brief Default constructor.
             */
    	Complexe();
    /********************************* DESTRUCTOR *********************************/
    	/**
             * @brief Default destructor.
             */
    	virtual ~Complexe();
    /*********************************** METHODS **********************************/
    	virtual double calculation(double& a, double& b);
    	virtual void function1(double& a);
    	void function2(double& a);
    /*********************************** GETTERS **********************************/
    private:
    	double m_alpha;
    	double m_epsilon;
    	double m_sigma;
    	std::vector<double> m_angle;
     
    };
     
    #endif /* COMPLEXE_H_ */

    Code:

    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
    #include "Comlexe.h"
     
    double Comlexe::calculation(double a, double b)
    {
      double res = 0.0;
     
      function1(a);
      function2(a);
     
      res = a + m_inputs[b];
     
      return (res);
    }
     
    void Comlexe::function1(double& a)
    {
      int i=0;
      for(i=0;i<m_inputs.size();++i)
      {
        m_inputs[i] = m_sigma*a + m_epsilon;
      }
    }
     
    void Comlexe::function2(double& a)
    {
      int i = 0;
      for(i=0;i<m_inputs.size();++i)
      {
        a += m_alpha[i] + m_inputs[i];
      }
    }

    Classe MethodeUser
    Code:

    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
    #ifndef METHODEUSER_H_
    #define METHODEUSER_H_
     
    #include "Methode.h"
     
    class MethodeUser
    {
    public:
    /******************************** CONSTRUCTOR *********************************/
    	/**
             * @brief Default constructor.
             */
    	MethodeUser();
    /********************************* DESTRUCTOR *********************************/
    	/**
             * @brief Default destructor.
             */
    	virtual ~MethodeUser();
    /*********************************** METHODS **********************************/
    	double useMethode(Methode& m, double& a, double& b);
    /*********************************** GETTERS **********************************/
    private:
     
    };
     
    #endif /* METHODEUSER_H_ */

    Code:

    1
    2
    3
    4
    5
    6
    7
    #include "MethodeUser.h"
     
     
    double MethodeUser::useMethode(Methode& m, double& a, double& b)
    {
      return m.calculation(double a, double b);
    }

    Classe User
    Code:

    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
    #ifndef USER_H_
    #define USER_H_
     
    #include "Simple.h"
    #include "Complexe.h"
     
    #include "MethodeUser.h"
     
    class User
    {
    public:
    /******************************** CONSTRUCTOR *********************************/
    	/**
             * @brief Default constructor.
             */
    	User();
    	User(MethodeMode mode);
    /********************************* DESTRUCTOR *********************************/
    	/**
             * @brief Default destructor.
             */
    	virtual ~User();
    /*********************************** METHODS **********************************/
    	void solve();
    /*********************************** GETTERS **********************************/
    private:
    	ModelUser m_modelUser;
    	Simple m_methodeSimple;
    	Complexe m_methodeComplexe;
    	MethodeMode m_mode;
    };
     
    #endif /* USER_H_ */

    Code:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #include "User.h"
     
    void User::solve()
    {
      double a = 3.14;
      double b = 1.51;
     
      if(m_mode==SIMPLE)
      {
        m_modelUser.useMethode(m_methodeSimple, a, b);
      }
      else if(m_mode==COMPLEXE)
      {
        m_modelUser.useMethode(m_methodeComplexe, a, b);
      }
      else
      {// errror: unknown mode
      }
    }

    Voilà le décor est posé.

    Je voudrais pouvoir faire :

    1. dans User.h, remplacer les attributs de méthodes par un seul attribut générique
    Code:

    1
    2
    3
    4
    private:
    	//Simple m_methodeSimple;
    	//Complexe m_methodeComplexe;
            Methode methode;

    Je pourrais le faire dans ce petit exemple, mais je pourrai plus appeler de méthodes propres aux classe filles.


    2. pouvoir appler des méthodes propres aux classe filles dans MethodeUser:
    Code:

    1
    2
    3
    4
    5
    6
    7
    8
    double MethodeUser::useMethode(Methode& m, double& a, double& b)
    {
        // par exemple initialiser m_angle qui appartient à Complexe
        std::vector<double> angle = std::vector<double>(SIZE, 13);
        m.initializeAngle(angle);
     
        return m.calculation(double a, double b);
    }

    pour eviter d'avoir deux fonctions qui font presque la même chose:
    Code:

    1
    2
    double MethodeUser::useMethode(Simple& m, double& a, double& b);
    double MethodeUser::useMethode(Complexe& m, double& a, double& b);

    Même je n'ai aucunes excues, je précise qu'à ma décharge, on m'avait dis que les (vraies) classes "Simple" et "Complexe" auraient exactement les mêmes caractéristiques, donc quand j'ai fais le design j'ai naturellement pensé à l'héritage.
    C'est par la suite que j'ai commencé à "bricoler" pour que tout ce tienne.
  • 22/02/2013, 10h27
    white_tentacle
    Tu veux faire des choses contradictoires :
    - ne pas conserver l’information de type réel de Methode en le considérant par sa classe mère
    - conserver l’information de type réel de Methode en appelant des méthodes spécifiques.

    Fatalement, ça ne marche pas.

    Plusieurs possibilités pour y remédier :
    - conserver l’information du type réel --> ça va t’amener à utiliser un variant (je préfère largement çà à du downcasting, qui marche aussi)
    - modifier l’interface de la classe mère pour que les traitements soient appelés indifféremment du type réel, quitte à ce qu’ils ne fassent rien (pas terrible)
    - utiliser un pattern visiteur
  • 22/02/2013, 12h54
    LinuxUser
    Citation:

    Envoyé par white_tentacle Voir le message
    Tu veux faire des choses contradictoires :
    - ne pas conserver l’information de type réel de Methode en le considérant par sa classe mère
    - conserver l’information de type réel de Methode en appelant des méthodes spécifiques.

    C'est exactement ce que je cherche à faire (malheureusement).

    Citation:

    Envoyé par white_tentacle Voir le message
    - conserver l’information du type réel --> ça va t’amener à utiliser un variant (je préfère largement çà à du downcasting, qui marche aussi)

    Ca impliquerait une duplication du code en doublant plusieurs fonctions (une pour chaque type réel).
    Je vois pas en quoi le downcasting(qui m'interesse pas non plus) est pire, j'aurais pensé qu'il vaille mieux caster que créer des doublons.
    Citation:

    Envoyé par white_tentacle Voir le message
    - modifier l’interface de la classe mère pour que les traitements soient appelés indifféremment du type réel, quitte à ce qu’ils ne fassent rien (pas terrible)

    En effet, pas terrible du tout, je passe mon chemin.

    Citation:

    Envoyé par white_tentacle Voir le message
    - utiliser un pattern visiteur

    La solution là voilà donc, je vais devoir m'y mettre à présent.

    Merci beaucoup en tout cas de votre aide.
  • 22/02/2013, 13h03
    Médinoc
    Mais le pattern visiteur implique de surcharger selon les types réels, au moins à un point.
  • 22/02/2013, 15h56
    Emmanuel Deloget
    J'ai du mal à saisir le code. Il n'y aurait pas un ModelUser qui se serait transformé en MethodUser (ou l'inverse) ?

    Ceci dit, ce que tu brises, ce n'est pas tant le LSP qu'un "principe" beaucoup plus fondamental : l'encapsulation. Pourquoi est-ce que ça serait à MethodeUser de savoir comment doivent se comporter les classes filles de Methode ? Si tu mets toutes les fonctions (exceptées calculation) privées, tu peux écrire le code que tu souhaites - en poussant la difficulté dans les classes filles, et non pas dans l'appelant.

    Ou y-a-t-il quelque chose que tu ne nous a pas dit ? :)
  • 22/02/2013, 16h04
    white_tentacle
    Citation:

    Envoyé par LinuxUser Voir le message
    Je vois pas en quoi le downcasting(qui m'interesse pas non plus) est pire, j'aurais pensé qu'il vaille mieux caster que créer des doublons.

    Je réponds juste à ce point, mais la différence fondamentale, c’est que :
    - le nombre de classes pouvant dériver d’une mère est infini et inconnu à la compilation (du moins en c++)
    - le nombre de types contenus dans un variant est fini et connu à la compilation

    Dans le premier cas (downcast), tu as la certitude que tu vas un jour « oublier » de gérér un type que tu rajoutes à un endroit dans ton code.

    Dans le deuxième cas, ton compilateur (avec les bonnes options évidemment, mais tu fais les choses bien ;) ) va te péter des avertissements et te prémunir d’erreurs.