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 :

templates et héritage


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2016
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Janvier 2016
    Messages : 11
    Par défaut templates et héritage
    bonjour, c'est mon 1er post ici, alors bonne année à tous les codeurs !
    Je n'arrive pas à trouver de solution au problème suivant.

    J'ai une hiérarchie de classes. Je dit hiérarchie car chaque dérivation ajoute des services au parent, on est bien dans le "est-un".
    Toutes ces classes utilisent une std::list de structures, des méthodes de manipulation de la liste sont communes et serait bien placées dans la classe de base ancêtre.
    Mais chaque dérivée utilise sa propre struct, différente de celle qu'utilise son parent.
    J'ai pensé faire une classe de base abstraite telle que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class Base<T> {
      std::list<T> mList;
      // methods for manipulating the list
    };
    et créer une dérivée par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    struct SA { ... };
    class A : public Base<SA> { ... }
    Le problème est que je ne peux plus dériver cette classe en lui faisant utiliser des struct SB à la place des SA.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    class B : public A { ... } // Base::mList est une list<SA>, moi je voulais SB
    class C : public Base<SC> { ... } // mList OK, mais je ne derive pas de A
    J'ai tourné ça dans tous les sens, essayé d'utiliser une classe annexe, utilisé l'héritage multiple, cherché sur le Net et je n'y arrive pas !
    Si quelqu'un voit comment faire...

    a+
    Gilles.

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Tu pars déjà d'un "bon point de vue" en réfléchissant à tes classes d'après les services qu'elles rendent plutôt qu'en y réfléchissant sous la forme des données qu'elles manipulent. Mais, voilà, ce n'est pas forcément parce que deux classes exposent les mêmes services (quitte à ce que l'une des deux en exposent quelques uns de plus) qu'il y a forcément une relation IS-A entre les deux (ni même que le LSP sera automatiquement respecté si on essaye de créer cette relation).

    Car les règles à respecter pour pouvoir envisager un héritage publique sont assez strictes : il faut que les invariants valides pour la classe de base soient également valides pour la classe dérivée. Or, si la classe de base utilise une donnée A en interne, c'est considéré comme "un invariant de la classe de base", qui doit donc être respecté dans la classe dérivée.

    Si bien que l'on peut très bien considérer que la classe dérivée manipule en interne des données de type B en plus des données de type A (que la classe de base manipule), et que le LSP est respecté, mais que, si la classe dérivée ne manipule que des données de type B, le LSP ne sera plus respecté

    A ce stade, la solution passe par le respect d'un autre des principes SOLID : l'ISP (pour Interface Segregation Principle ou principe de ségrégation des interfaces).

    L'idée est de veiller à chaque fois à garder les interfaces les plus simples possibles, si bien que ce que tu identifie ici comme ta classe "Base" deviendrait en réalité une "interface" te permettant "de manipuler une collection d'éléments dont on ignore tout jusqu'à présent", par exemple, en prenant une forme proche de
    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
    /* Interface permettant de gérer les collections d'éléments
     *
     * utilise le CRTP pour éviter d'avoir des hiérarchies trop importantes
     * tparam CRTP type dérivé de l'interface (pour la facilité par la suite)
     * tparam DATA type des données à placer dans la collection
     */
    template<typename CRTP, typename DATA>
    class ICollectionHolder{
    public:
       /* les fonctions d'ajout, de suppression, de recherche,  etc
        * qui manipulent toutes plus ou moins toujours des données de type
        * DATA
        * peut être un ou deux alias(es) de type sur les itérateurs ?
        */
    protected:
        /* constructeur et destructeur (non virtuel) protégés
         * pour nous forcer à utiliser l'héritage
         */
        ICollectionHolder(CRTP & crtp):crtp_(crtp){}
        ~ICollectionHolder(){}
    private:
        /* nous permet, en cas de besoin, d'accéder à l'instance du type
         * réel qui implémente l'interface
         */
        CRTP & crtp_;
        /* les éléments qu'il faut maintenir ensembles */
        std::list<DATA> datas_;
    };
    De la même manière, tu pourrais regrouper les différents ensembles de fonctionnalités dans différentes interfaces, par exemple :
    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
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
     
    /* utilise le CRTP pour éviter les hiérarchies monolithiques
      */
    template <typename CRTP>
    class IDrawable{
    public:
        void draw() const; // fournir une spécialisation de cette fonction pour chaque
                                     // type susceptible d'être tracé
     
    protected:
        /* constructeur et destructeur (non virtuel) protégés
         * pour nous forcer à utiliser l'héritage
         */
        IDrawable(CRTP & crtp):crtp_(crtp){}
        ~IDrawable(){}
    private:
        /* nous permet, en cas de besoin, d'accéder à l'instance du type
         * réel qui implémente l'interface
         */
        CRTP & crtp_;
    };
    /* interface permettant le déplacement des éléments qui peuvent l'être
      * utilise aussi le CRTP
      */
    template <typename CRTP>
    class IMovable{
    public:
        /* à pmplémenter pour chaque type implémentant cette interface */
        void moveTo(Position const & newPos);
        // OU   - OU   -   OU
        void move(Type diffx, Type diffY /* , Type diffZ ??? */);
     
    protected:
        /* constructeur et destructeur (non virtuel) protégés
         * pour nous forcer à utiliser l'héritage
         */
        IMovable(CRTP & crtp):crtp_(crtp){}
        ~IMovable(){}
    private:
        /* nous permet, en cas de besoin, d'accéder à l'instance du type
         * réel qui implémente l'interface
         */
        CRTP & crtp_;
        Position pos_;
    };
    /* on pourrait encore envisager d'autres interfaces, par exemple, la possibilité de regrouper
      * IMovable et IDrawable
      */
    template <typename CRTP>
    class MoveAndDraw : public IDrawable<CRTP>,
                                     public IMovable<CRTP>{
    protected:
        MoveAndDraw(CRTP & crtp):IDrawable<CRTP>(crtp), IMovable<CRTP>(crtp){}
    };
    Enfin, si cela a du sens, tu peux envisager une hiérarchie de classe proche de
    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
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    class Base : public ICollectionHolder<Base, SomeType>{
    public:
        Base():ICollectionHolder<Base, SomeType>(*this){
        }
        virtual ~Base();
    };
    /* un élément qui maintient une collection de SomeType, qui peut être tracé, mais pas déplacé
     */
    class Derived1 : public Base,
                             public IDrawable<Derived1>{
    public:
        Derived1():Base(),IDrawable<Derived1>(*this){
        }
    };
    /* un élément qui maintient une collection de SomeType qui peut être déplacé mais pas tracé
     */
    class Derived2 : public Base,
                             public IMovable<Derived2>{
    public:
        Derived2():Base(),IMovable<Derived2>(*this){
        }
    };
    /* un élément qui maintient une collection de SomeType qui peut être déplacé et tracé
     */
    class Derived3 : public Base,
                             public MoveAndDraw<Derived3>{
    public:
        Derived3():Base(), MoveAndDraw<Derived3>(*this){
        }
    };
    /* un toute autre hiérarchie de classes... tous les élément maintiennent une collection de OtherType!!!
     */
    class OTBase : public ICollectionHolder<OtherType>{
    public:
        OTBase():ICollectionHolder<OtherType>(*this){
        }
        virtual ~OTBase();
    };
     
    /* un élément qui maintient une collection de OtherType, qui peut être tracé, mais pas déplacé
     */
    class OTDerived1 : public OTBase,
                             public IDrawable<OTDerived1>{
    public:
        OTDerived1():Base(),IDrawable<OTDerived1>(*this){
        }
    };
    /* un élément qui maintient une collection de OtherType qui peut être déplacé mais pas tracé
     */
    class OTDerived2 : public OTBase,
                             public IMovable<OTDerived2>{
    public:
        OTDerived2 ():Base(),IMovable<OTDerived2 >(*this){
        }
    };
    /* un élément qui maintient une collection de OtherType qui peut être déplacé et tracé
     */
    class OTDerived3 : public OTBase,
                             public MoveAndDraw<OTDerived3>{
     
    public:
        OTDerived3  ():Base(),MoveAndDraw<OTDerived3 >(*this){
        }
    };
    /* un élément qui peut être tracé, mais qui ne contient aucune collection d'objet et qui 
     * ne peux pas être déplacé
     */
    class NoItemD : public IDrawable<NoItemD>{
    public:
        NoItemD():IDrawable<NoItemD>(*this){
       }
     
    };
    /* etc */
    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

  3. #3
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2016
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Janvier 2016
    Messages : 11
    Par défaut
    merci pour cette réponse détaillée.
    Bon, je ne vois pas trop comment l'appliquer à mon cas, je n'ai pas l'habitude de ces pirouettes :-)
    Et il y a quelque chose qui me gêne : je ne peux pas faire une dérivée d'une dérivée.

    Ma classe de base est une AudioSource, elle est abstraite. Elle fournit des blocs d'audio à un moteur audio.
    Je la dérive d'abord en SeekableAudioSource, capable de se "déplacer" dans un fichier audio ( SetPosition(), Rewind()...)
    Je la dérive ensuite en CyclableAudioSource, permettant de lire le fichier audio en boucle sur une plage donnée ( SetLimits(), SetCycle(on/off)...)
    Enfin je la dérive en StretchableAudioSource, capable de changer la vitesse de lecture (SetSpeed()...)

    Chaque niveau apporte de nouveaux membres. Si StretchableAudioSource ne dérive pas de CyclableAudioSource, je dois recopier les méthodes du Cyclable dans le Stretchable ?

    J'ai omis de préciser que les struct mises en liste ne sont pas quelconques : une dérivée utilise la struct de son parent, augmentée de nouveaux membres.
    Ainsi la classe Seekable (la + haute) utilise
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    struct SeekableEvent {
      double time;
      int position;
    }
    le Cyclable, dérivé de Seekable, utilise
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    struct CyclableEvent {
      double time;
      int position;
      int position_min;
      int position_max;
    }
    Il apparaît que les struct utilisées par la hiérarchie des classes présentent elles-mêmes une hiérarchie. Ne peut-on tirer parti de cela ?
    (j'en tire déjà profit en ayant une seule méthode template pour, par ex., supprimer les éléments de la liste dont le champ "time" est trop vieux)

  4. #4
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 154
    Billets dans le blog
    4
    Par défaut
    Pourquoi compliquer autant ?
    Pourquoi ne pas avoir une seule classe AudioSource avec toutes ces fonctionnalités ?
    Quand j'utilisais FMOD, il n'y a qu'une seule classe Audio qui possède tout ça (position, rewind, loop ..), je vois pas bien l'intérêt de découper ainsi.

    Avec de telles structures y'a bien des solutions à base de void* et cast, à l'ancienne en C, mais la question sur l'intérêt de la chose l'emporte dans mon esprit.

    Mais un truc comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    template<class STRUCT> class Base {};
    template<class SEEK_STRUCT = SeekableEvent> class Seekable : public Base<SEEK_STRUCT> {];
    template<class CYCLABLE_STRUCT = CyclableEvent> class Cyclable : public Seekable<CYCLABLE_STRUCT> {};
    pourrait marcher ?
    Mais j'ai l'impression que tu compliques inutilement trop alors qu'un son possède toutes ses cractéristiques en général.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  5. #5
    Membre Expert
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Par défaut
    @Bousk, le problème serait avec une fonction prenant un Seekable en argument. On pourrait pas lui donner un Cyclable vu qu'ils n'ont pas de base commune.

    Sinon, oui une seule classe contenant tout pourrait être une solution.

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 154
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par Iradrille Voir le message
    @Bousk, le problème serait avec une fonction prenant un Seekable en argument. On pourrait pas lui donner un Cyclable vu qu'ils n'ont pas de base commune.
    En ce cas, pourquoi ces structures n'ont pas une vraie hiérarchie en héritage ? Plutôt que de recopier les membres du "parent".
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  7. #7
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2016
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Janvier 2016
    Messages : 11
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Pourquoi compliquer autant ?
    Pourquoi ne pas avoir une seule classe AudioSource avec toutes ces fonctionnalités ?
    En fait, j'écris une librairie que je compte publier en open-source, comme composant wxCode.
    La librairie impose la classe virtuelle AudioSource, ce sera à l'utilisateur de ma librairie d'écrire sa classe dérivée de AudioSource.

    Avant de publier ça, d'une part je voudrais tester le concept, d'autre part fournir quelques classes AudioSource comme exemple.
    C'est mal barré s'il faut utiliser des templates récursifs ou autres constructions de haut niveau, les utilisateurs (s'il y en a ) seront découragés...
    Je pourrais me contenter de fournir la SeekableAudioSource de 1er niveau, qui gère tranquillement sa liste de SeekableEvent, sans templates. Si l'utilisateur part de cet exemple, il ne pourra pas dériver cette classe, c'est pas sympa.

  8. #8
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par biggil Voir le message
    En fait, j'écris une librairie que je compte publier en open-source, comme composant wxCode.
    La librairie impose la classe virtuelle AudioSource, ce sera à l'utilisateur de ma librairie d'écrire sa classe dérivée de AudioSource.
    J'ai envie de dire : raison de plus !!! Tu rends ta classe AudioSource abstraite si tu veux, mais tu dois absolument veiller à ce qu'elle expose tous les services que l'on est en droit d'attendre de la part d'une source audio!!!

    Au final, si l'on prend la hiérarchie à base de AudioSource que j'ai présentée dans mon intervention précédente, la seule classe dont l'utilisateur de ta bibliothèque aura besoin (quitte à en faire une classe abstraite, encore une fois) correspond à StretchableAudioSource, qui devrait être "simplement" connue sous le nom de AudioSource
    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

  9. #9
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par biggil Voir le message
    merci pour cette réponse détaillée.
    Bon, je ne vois pas trop comment l'appliquer à mon cas, je n'ai pas l'habitude de ces pirouettes :-)
    Et il y a quelque chose qui me gêne : je ne peux pas faire une dérivée d'une dérivée.
    Bien sur que si, que tu peux faire une dérivée de dérivée :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    template <typename CRTP
    class ISomeOtherInterface{
    public:
       void doSomething();
    protected:
        ISomeOtherInterface(CRTP & crtp):crtp_(crtp){}
        ~ISomeOtherInterface(){}
    private:
        CRTP & crtp_;
    };
    class DerivedX : public Derived1,
                             public ISomeOtherInterface<DerivedX>{
    };
    Ma classe de base est une AudioSource, elle est abstraite. Elle fournit des blocs d'audio à un moteur audio.
    Je la dérive d'abord en SeekableAudioSource, capable de se "déplacer" dans un fichier audio ( SetPosition(), Rewind()...)
    Je la dérive ensuite en CyclableAudioSource, permettant de lire le fichier audio en boucle sur une plage donnée ( SetLimits(), SetCycle(on/off)...)
    Enfin je la dérive en StretchableAudioSource, capable de changer la vitesse de lecture (SetSpeed()...)
    OK, au moins, maintenant, on sait de quoi l'on parle

    Chaque niveau apporte de nouveaux membres. Si StretchableAudioSource ne dérive pas de CyclableAudioSource, je dois recopier les méthodes du Cyclable dans le Stretchable ?
    Ben, si tous les élément "cyclable" sont "seekable" et qu'il présentent les mêmes services, autant faire en sorte que cyclable dérive de seekable
    J'ai omis de préciser que les struct mises en liste ne sont pas quelconques : une dérivée utilise la struct de son parent, augmentée de nouveaux membres.
    Ainsi la classe Seekable (la + haute) utilise
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    struct SeekableEvent {
      double time;
      int position;
    }
    le Cyclable, dérivé de Seekable, utilise
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    struct CyclableEvent {
      double time;
      int position;
      int position_min;
      int position_max;
    }
    AAhhh, voilà ton erreur!!! Tu crées deux structures qui contiennent des données similaires et qui t'obligent donc à choisir entre ces deux structures...

    Pourquoi ne pas, simplement, avoir la structure SekableEvent telle qu'elle est maintenant et faire en sorte que ta structure CyclableEvent ne rajoute que les données manquantes pour être en mesure de fournir les services attentus
    tu aurais donc:
    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
    struct SeekableEvent {
      double time;
      int position;
    };
    struct CyclableEvent {
      int position_min;
      int position_max;
    };
    class AudioSource{
        /*...*/
    };
    class SeekableAudioSource : public AudioSource{
    /*...*/
    private:
        SeekableEvent seek_;
    };
    class CyclableAudioSource : public SeekableAudioSource{
    /* ...*/
    private:
        CyclableEvent  cycle_;
    };
    class StretchableAudioSource : public CyclableAudioSource{
    /* ... */
    private:
        Type speed_;
    };
    On pourrait même prévoir les interfaces sous la forme de
    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
    template <typename CRTP>
    class ISeekable {
    /* ... */
    private:
        SeekableEvent seek_;
    };
    template <typename CRTP>
    class ICyclable {
        static_assert(std::is_base_of<ISeekable<CRTP>,CRTP>,
                             "Cyclable items have to be seekable");
        /*...*/
    private:
        CyclableEvent  cycle_;
    };
    template <typename CRTP>
    class ISpeedable{
     
        static_assert(std::is_base_of<ICyclable<CRTP>,CRTP>,
                             "speedable items have to be cyaclable");
    /* ... */
    private:
        Type speed_;
    };
    Ceci dit, je suis d'accord avec les autres: tu n'as besoin que d'une seule classe : AudioSource qui expose l'ensemble des services que tu es en droit d'attendre de la part de ce type de donnée (autrement dit : les services exposés par l'ensemble des interfaces que je viens de présenter). Il n'y a absolument aucune raison pour essayer de créer des classes qui en dérivent

    Par contre, je peux effectivement comprendre que cela t'embête plus ou moins d'avoir une classe unique se présentant sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class AudioSource{
    public:
        /* tout ce qui a trait au chargement des données */
        /* tout ce qui a trait au déplacement dans la source */
        /* tout ce qui a trait au passage en boucle */
        /* tout ce qui a trait au changement de vitesse*/
        /* ... */
    private:
        double time;
        int position;
        int position_min;
        int position_max;
        Type speed;
    };
    car je peux comprendre que cela risquerait de faire énormément de fonctions, et que l'on puisse "avoir peur d'en oublier". C'est, d'une certaine manière, tout l'intérêt des interfaces : on peut se concentrer sur un nombre de services particulièrement restreint, et il n'y a absolument rien qui nous interdise de décider qu'une unique classe puisse implémenter toutes les interfaces

    Il faut juste arriver à répondre en toute honnêteté à la question : n'est on pas occupés à basculer dans l'over-engeeniering
    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

Discussions similaires

  1. Template et héritage
    Par Ulmo dans le forum Langage
    Réponses: 16
    Dernier message: 08/03/2009, 23h41
  2. Template et héritage
    Par rooger dans le forum Langage
    Réponses: 5
    Dernier message: 22/07/2008, 13h48
  3. Foncteur, classes templates et héritage
    Par Floréal dans le forum C++
    Réponses: 8
    Dernier message: 17/06/2007, 21h56
  4. Template ou héritage
    Par bolhrak dans le forum Langage
    Réponses: 6
    Dernier message: 22/12/2006, 11h22
  5. patron, templates et héritages!
    Par kleenex dans le forum C++
    Réponses: 4
    Dernier message: 05/06/2006, 22h57

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