IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Données virtuelles - hiérarchies de classes parallèles


Sujet :

Langage C++

  1. #1
    Invité
    Invité(e)
    Par défaut Données virtuelles - hiérarchies de classes parallèles
    Bonjour,

    Ceci est probablement une question bateau, mais je sêche...

    J'ai deux hiérarchies de classes parallèles, l'une de conteneurs, l'autres d'élements de ces conteneurs, quelque chose comme

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    class TConteneur {
      vector<TElement*> Elements;
    };
     
    class TElement {
      TConteneur *Parent;
    };
    (NB je mets des pointeurs, mais ca pourrait être n'importe quoi...)

    De cette façon un conteneur connait ses éléments, un élément son conteneur, tout va bien.

    Maintenant, je veux spécialiser conteneurs et élements, en les associant 2 à 2...

    Par exemple

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    class TConteneurSpecial : public TConteneur {
     ...
    };
     
    class TElementSpecial : public TElement {
      ...
    };
    Mon problème est le suivant : je voudrais que pour un TElementSpecial, Parent pointe bien vers un TConteneurSpecial, et pas vers un TConteneur normal...

    Je vois à ce stade trois solutions :

    1- mettre des casts partout... c'est pas trop classe, mais ca va marcher
    2- cacher ce cast dans une méthode GetParent(), surcharchée dans TConteneurSpecial :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    TConteneur *TElement::GetParent() {return Parent;}
    TConteneurSpecial *TElementSpecial::GetParent() {return (TConteneurSpecial*)Parent;}
    3- cacher le cast dans une donnée membre

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    class TElementSpecial : public TElement {
      TConteneurSpecial *ParentSpecial;
    };
    Je précise que Parent est un membre privé, que je ne vais pas avoir d'appels hors des fonctions de la classe TConteneur.

    Aucune de ces trois méthode ne me gêne particulièrement, je pense qu'elles marchent toutes (je vais vérifier la seconde), mais aucune ne me semble non plus très élégante...

    Comment feriez vous?

    Merci d'avance
    Francois

  2. #2
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Points : 4 732
    Points
    4 732
    Par défaut
    Par quoi diffèrent tes classes enfants de tes classes parentes ?

    sinon, je ne vois pas bien l'intérêt de maintenir 2 hiérarchies parallèles: l'une pour le conteneur, l'autre pour le contenu. Peut tu expliciter pourquoi tu as besoin d'une telle chose ? Car le but d'un conteneur est d'être transverse au contenu.
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

  3. #3
    Invité
    Invité(e)
    Par défaut
    Conteneurs et éléments sont très différents. Grosso modo, on est dans un modèle vue documents...

    Tous les conteneurs se ressemblent et dérivent d'une classe commune, pareil pour les éléments... Mais aussi, il y a toutes sortes d'éléments, qui sont en fait associées à des conteneurs différents. Donc il y a une hierarchie d'élements, comme une hiérarchie de conteneurs, et des liens entre ces deux hierarchies, puisque les conteneurs sont (justement) destinés aux éléments

    Je dis que c'est un problème bateau, parce qu'on le rencontre couramment dans les interfaces : on a par exemple des rapports, et des tableaux, et puis, des rapports financiers, qui vont contenir des tableaux financiers... J'arrive à voir des solutions compliquées, notamment en utilisant une troisieme classe/hierarchie, mais je me demande si on ne peut trouver qq chose de simple...

    Francois

  4. #4
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Une solution sont les classes traits (mais je suis comme david, un peu intrigué par ton problème) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    template<class ConteneurT, class ElementT>
    struct ConteneurElementTrait;
    spécialisé pour chaque paire :
    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
     
    template<>
    struct ConteneurElementTrait<CConteneur1, CElement1>
    {
       typedef CConteneur1 conteneur;
       typdef CElement1 element;
    };
     
    template<>
    struct ConteneurElementTrait<CConteneur2, CElement2>
    {
       typedef CConteneur2 conteneur;
       typdef CElement2 element;
    };
     
    template<ConteneurElementTrait>
    class TConteneur {
      vector<typename ConteneurElementTrait::element*> Elements;
    };
     
    template<ConteneurElementTrait>
    class TElement {
      typename ConteneurElementTrait::conteneur*Parent;
    };
    Si tu veux un typage fort tu est obligé par une vérification statique -> générique.
    Si tu ne veux pas passer par du générique, alors je ne vois pas d'autres solutions que de faire une vérification dynamique du type effectif (typeid/dynamic_cast).

  5. #5
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    J'ai un gros probleme avec le fait que tu fais un heritage public qui contraint la classe de base a respecter un invariant supplementaire.

    Suivant ce que tu veux faire, il y a des organisations possibles, du genre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    template <typename T> class Conteneur
    {
       vector<T*> Elements;
    };
     
    template <typename T> class Element
    {
       Conteneur<T> Parent;
    };
     
    class ElementSpecial: public Element<ElementSpecial> {
    };
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  6. #6
    Invité
    Invité(e)
    Par défaut
    Bonsoir,

    Merci pour ces réponses, je vais expliquer un peu plus...

    La réalisation au travers d'une hiérarchie de classes, avec héritage public, et sans templates n'est pas réellement négociable... Les objets manipulés ici sont des éléments d'interface, appartenant à une hiérarchie complexe (en gros la VCL de Borland), qui gère beaucoup de choses que je n'ai pas du tout l'intention de gérer à la main.

    L'image la plus ressemblante que je peux trouver pour mes conteneurs et mes éléments, c'est une notion de rapports, qui contiendraient des documents.

    Disons que, en gros, l'application gère des rapports, qui contiennent des documents. Il y a plusieurs sortes de documents, et plusieurs sortes de rapports.

    Cependant, tous les rapports ont des propriétés semblables (comme être composés de documents, ou savoir s'imprimer, s'afficher, s'exporter vers un tableur, se mettre à jour...), et partagent des données et paramètres communs (de date, ou informations métier). L'idée est donc de les faire tous descendre d'une même classe abstraite.

    De même, tous les documents ont un certain nombre de propriétés en commun, et descendent donc d'une même classe.

    On retrouve donc là ma classe TConteneur, qui contient un vecteur de TElement, et mes TElement qui ont un pointeur vers leur TConteneur...

    Jusque là, on est dans une structure hiérarchique banale.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    class TConteneur {
       // propriétés spécifiques aux conteneurs
       // propriétés communes à tous les éléments
       vector<TElement *> Elements;
    };
     
    class TElement {
      // propriétés specifiques à l'élément
      TConteneur *Parent;
    }
    Mon vecteur d'élements permet à mon conteneur d'itérer sur les éléments qui le composent (par exemple pour les imprimer...), et mon lien Parent permet à mes éléments de retrouver dans leur conteneur les paramètres de contexte qui les relient entre eux.

    Mais cela se complique quand on dérive conteneurs et éléments... En fait, certains éléments particuliers (classes dérivées de TElement) s'assemblent sous la forme de conteneurs particuliers, qui contiennent, en particulier des données communes spécifiques. J'ai alors

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class TElementParticulier : public TElement {
      // propriétés supplémentaires
    }
     
    class TConteneurParticulier : public TConteneur {
      // propriétés communes supplémentaire,s partagées par tous les éléments
      // qui d'ailleurs dérivent tous de TElementParticulier
    }
    Le problème dans ce cas, c'est de permettre aux TElementParticulier d'accéder aux données communes de TConteneurParticulier.

    Si j'utilise le pointeur Parent de la classe parente TElement, il va me rendre un
    TConteneur*, je ne vais donc pas voir les propriétés additionnelles de la classe parente.

    A ce stade, je peux donc :

    1- ajouter un pointeur dans TElementParticulier, TConteneurParticulier *ParentParticulier, qui remplacera, dans les faits le pointeur Parent de tout à l'heure
    2- faire un bon vieux cast du Parent
    3- utiliser une fonction, surchargée dans chaque classe, pour renvoyer le parent.

    Aucune de ces trois méthodes ne me parait très élégante. Je me demande donc s'il y a mieux. Actuellement, je penche pour la 1-, moins verbeuse que le cast, mais aussi rapide (à la différence de la surcharge)

    Francois

  7. #7
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par fcharton Voir le message
    1- ajouter un pointeur dans TElementParticulier, TConteneurParticulier *ParentParticulier, qui remplacera, dans les faits le pointeur Parent de tout à l'heure
    2- faire un bon vieux cast du Parent
    3- utiliser une fonction, surchargée dans chaque classe, pour renvoyer le parent.
    Si tu restes dans cette optique, alors j'aurais tendance à préférer le 3, la fonction surchargée assurant par un retour covariant le type attendu.
    A titre d'indication, tu as ce genre de chose dans le MVC des MFC :
    CView -> CDocument* GetDocument( ) const;
    CMyView -> CMyDocument*GetDocument( ) const;

    Je ne sais si je dois comprendre la 1 comme
    -> un TConteneur * dans TElement ET un TConteneurParticulier * dans TElementParticulier
    -> ou plus rien dans TElement, et TConteneurParticulier * dans chaque nouveau TElementParticulier
    Mais ces deux optiques me paraissent pas terribles.

    La solution 3 est la solution 2 mais avec le cast à un seul endroit. Sur des composants IHM, je ne suis pas persuadé que le cout de l'indirection soit prohibitif.

Discussions similaires

  1. Hiérarchie de classe parallèles.
    Par piemur2000 dans le forum Design Patterns
    Réponses: 2
    Dernier message: 30/03/2010, 23h16
  2. base de données, hibernate, diagramme de classes
    Par tidar dans le forum Hibernate
    Réponses: 1
    Dernier message: 22/01/2007, 23h05
  3. Hiérarchie de class et membres spécifiques
    Par bolhrak dans le forum C++
    Réponses: 6
    Dernier message: 23/10/2006, 13h23
  4. Réponses: 2
    Dernier message: 25/08/2006, 22h18
  5. Réponses: 3
    Dernier message: 24/04/2005, 14h19

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo