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

C++ Discussion :

interface et héritage multiple


Sujet :

C++

  1. #1
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut interface et héritage multiple
    Bonjour à tous.
    J'ai un problème que je ne comprends pas et dont j'ai une solution qui ne me plait pas du tout.

    Soient trois d'interfaces avec implémentation par défaut:
    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
     
    struct BaseSimple {
        virtual ~BaseSimple() = 0;
        virtual void simple() { cout << "BaseSimple" << endl; }
    };
     
    struct BaseRiche: public BaseSimple {
        virtual ~BaseRiche() = 0;
        virtual void riche() {}
    };
     
    struct Biduloide {
        virtual ~Biduloide() = 0;
        virtual void operator() () {}
    };
    Les trois destructeurs sont définis à l'abri des fichiers .cpp.

    Jusque là, tout va bien, j'ai trois classes abstraites, dont un héritage.

    Viennent ensuite deux classes qui ne pose pas encore problème.
    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
    class Simple: public BaseSimple, public Biduloide {
    public:
        virtual void simple() override final {
            /* un peu d'intelligence */
            do_simple();
        }
    protected:
        virtual void do_simple() {}
    };
     
     
    class MoinsSimple: public Simple {
    protected:
        virtual void do_simple() override final { cout << "MoinsSimple" << endl;}
    };
    Si à présent j'écris ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    BaseSimple * s = new MoinsSimple();
    s->simple();
    J'espère bien voir s'afficher "MoinsSimple", puisque c'est ainsi que doit fonctionner les fonctions virtuelles.
    En effet, s->simple() est Simple::simple(), qui appelle Simple::do_simple(), virtuelle donc trouvée dans MoinsSimple.

    Ajoutons une nouvelle classe:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class Riche: public BaseRiche, public MoinsSimple {
    public:
        using MoinsSimple::simple
    };
    D'après le standard, j'ai le layout suivante: <Riche> = < <<BaseSimple> BaseRiche> <<<BaseSimple> Simple> MoinsSimple> >.
    Je vois bien que j'ai deux BaseSimple.
    J'ai ainsi trois définitions de simple(): deux dans les BaseSimple, et une dans Simple (cette dernière version étant marquée final)

    Si ma lecture était bonne, simple serait ambigüe dans Riche, donc j'ai écris un using MoinsSimple::simple.
    Or, ce n'est pas le cas.
    Le code suivant affiche "BaseSimple"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    BaseRiche * s = new Riche();//pour des raisons de smart_ptr à partager un peu partout
    s->simple();
    EDIT: dans mon vrai problème le pointeur n'est pas Riche* mais BaseRiche*

    J'ai bien une solution en rendant Simple::simple() non finale, et en écrivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class Riche: public BaseRiche, public MoinsSimple {
    public:
        virtual void simple() override final {MoinsSimple::simple();}
    };
    Mais je n'ai pas de raison de le faire, puisque c'est précisément pour la rendre final que j'ai créé do_simple().

    Je pourrai peut-être m'en sortir avec de l'héritage virtuel, mais j'aimerai surtout comprendre pourquoi j'ai ce problème.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  2. #2
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Hello leternel

    Avec quel compilo cela pose-t-il problème ? Je viens de tester avec GCC 4.8, GCC 5.2, Clang 3.4, Clang 3.8 et tous se comportent bien de la manière que tu attends. Mais pas Visual Studio qui a le comportement que tu cites... A voir ce que ça donne sur la dernière Preview, je n'ai pas encore pu tester.

    Je pense que tu as affaire à un bug de cl.exe
    Find me on github

  3. #3
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    En l'occurence, gcc version 4.8.2 20140120 (Red Hat 4.8.2-16) (GCC)

    Je suis allé un peu vite dans ma simplification de l'exemple.

    BaseRiche * s = new Riche(); s->simple(); produit "BaseSimple", avec ou sans using.
    Sans using, Riche * s = new Riche(); s->simple(); ne compile pas (ambigu), et fonctionne comme souhaité avc.

    La solution, si je ne veux pas lever le final, c'est l'héritage virtuel.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  4. #4
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    C'est vraiment étonnant comme comportement, je me demande ce qu'en dit la norme, mais il nous faudrait un avocat, c'est un peu subtil dans ce cas. C'est très décevant
    Find me on github

  5. #5
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 470
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 470
    Points : 6 107
    Points
    6 107
    Par défaut
    Bonjour,

    leternel, ton exemple est équivalent à celui-ci, plus facile à lire :
    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
    // Code compilé ici : http://coliru.stacked-crooked.com/
     
    #include <iostream>
     
    struct A
    {
        virtual void foo() { std::cout << "A::foo()\n"; }
    };
     
    struct B : public A
    {
        void foo() override { std::cout << "B::foo()\n"; }
        virtual void bobo() {}
    };
     
    struct C : public A
    {
        virtual void coco() {}
    };
     
    struct D : public B, public C
    {
        void         bobo() override {}
        void         coco() override {}
        virtual void dodo()          {}
    };
     
    int main()
    {
        std::cout << "Version de g++ : " << __VERSION__ << "\n\n"; // version 6.1.0
        D d;
    //  d.foo();                  // erreur de compilation : foo est ambigu
    //  static_cast<A&>(d).foo(); // erreur de compilation : A est une classe de base ambigüe
        static_cast<B&>(d).foo(); // appelle B::foo()
        static_cast<C&>(d).foo(); // appelle A::foo()
        return 0;
    }
    Dans mon exemple, à mon avis, sous le capot, ça se passe comme ça :
    Dans un objet de type D :
    • L'objet B contient un objet A qui contient un pointeur vers une table virtuelle dont les premières adresses de fonction sont :
      1. adresse de B::foo
      2. adresse de D::bobo
    • L'objet C contient un objet A qui contient un pointeur vers une table virtuelle dont les premières adresses de fonction sont :
      1. adresse de A::foo (et non pas B::foo)
      2. adresse de D::coco
    • Au moins une des deux tables précédentes a pour 3e adresse de fonction D::dodo.


    Dans l'exemple de leternel :
    • simple() a le rôle de foo.
    • BaseSimple a le rôle de A.
    • Simple a le rôle de B.
    • BaseRiche a le rôle de C.
    • Riche a le rôle de D.

  6. #6
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Je vais chercher dans la norme les explications sur les tables de virtuel, le layout des classes et l'héritage multiple.
    L'explication semble bonne.
    D'autant que rendre virtuel l'héritage de la classe de base (BaseSimple ou A) supprime le problème (à un using près)
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  7. #7
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Là, tu tombe dans le coup classique de l'héritage en losange / en diamant (de la mort)...

    Typiquement, si tu as la possibilité de modifier les classes de base (BaseRiche et MoinsSimple), tu devrais en théorie avoir recours à l'héritage virtuel afin qu'il n'y ait effectivement qu'une seule instance de BaseSimple, autrement, VS a bien raison, il me semble que tu es effectivement face à un UB.

    Ceci dit, si tu as effectivement la possibilité de modifier tes classes de base, la véritable erreur est d'avoir fait en sorte que ce qui te sert d'interface hérite de ta classe de base : tu devrais avoir des interfaces 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
    class ISimple{
    public:
        ISimple(ISimple const & ) = delete; // comme tout ce qui a sémantique d'entité, on ne peut pas la copier
        ISimple & operator= (ISimple const & ) = delete // comme tout ce qui a sémantique d'entité, on ne peut pas l'assigner 
        virtual void simple(){
            std::cout<<"simple"
        }
    protected:
        ISimple(/* paramètre éventuels*/); // comme c'est une interface, on ne peut l'instancier qu'au travers des classes dérivées
        ~ISimple(); // Comme c'est une interface, on ne peut l'instancier qu'au travers des classes dérivées
    private:
        /* contrairement à java, on pourrait très bien avoir des données membre ici... 
         * pour autant que l'on ai la (quasi) certitudes qu'elles seraient toujours utiles
         */
    };
    /* Et on fait bien sur pareil pour l'interface de Riche */
    class IRiche{
     
    public:
        IRiche(IRiche const & ) = delete; // comme tout ce qui a sémantique d'entité, on ne peut pas la copier
        IRiche & operator= (IRiche const & ) = delete // comme tout ce qui a sémantique d'entité, on ne peut pas l'assigner 
        virtual void riche(){
            std::cout<<"riche"
        }
    protected:
        IRiche(/* paramètre éventuels*/); // comme c'est une interface, on ne peut l'instancier qu'au travers des classes dérivées
        ~IRiche(); // Comme c'est une interface, on ne peut l'instancier qu'au travers des classes dérivées
    private:
        /* contrairement à java, on pourrait très bien avoir des données membre ici... 
         * pour autant que l'on ai la (quasi) certitudes qu'elles seraient toujours utiles
         */
    };
    /* et pour Biduloide */
    class IBiduloide{
    public:
        IBiduloide(IBiduloide const & ) = delete; // comme tout ce qui a sémantique d'entité, on ne peut pas la copier
        IBiduloide & operator= (IBiduloide const & ) = delete // comme tout ce qui a sémantique d'entité, on ne peut pas l'assigner 
        virtual void operator()(){
        }
    protected:
        IBiduloide(/* paramètre éventuels*/); // comme c'est une interface, on ne peut l'instancier qu'au travers des classes dérivées
        ~IBiduloide(); // Comme c'est une interface, on ne peut l'instancier qu'au travers des classes dérivées
    private:
        /* contrairement à java, on pourrait très bien avoir des données membre ici... 
         * pour autant que l'on ai la (quasi) certitudes qu'elles seraient toujours utiles
         */
    };
    Du coup, comme il n'y a plus aucun lien d'héritage entre ces trois classes, tu n'as plus aucun problème : tu peux créer une classe de base 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
     
    class BaseSimple : public ISimple{ /*la classe de base n'expose que l'interface ISimple */
        /* tu en fais ce que tu veux (redéfinir simple(), peut être?  mais rien ne t'y oblige !!! */
    };
    class BaseRiche : public BaseSimple, public IRiche{ /* Dans BaseRiche, on ajoute l'interface IRiche à l'interface ISimple*/
     
        /* tu en fais ce que tu veux (redéfinir simple() ou riche(), peut être?  mais rien ne t'y oblige !!!) */
    };
    class Simple : public BaseSimple, public IBiduloide{ /* dans Simple, on ajoute l'inerface IBiduloide à l'interface ISimple */
     
        /* tu en fais ce que tu veux (redéfinir simple() ou operator()(), peut être?  mais rien ne t'y oblige !!!)
    };
    class MoinsSimple : public Simple{ /* la classe MoinSimple ne fait que redéfinir les comportements de Simple */
     
    };
    /* Pour Riche, il faut faire un choix : 
     * SOLUTION N°1 : On veut la mélanger avec d'autres objets "connus comme étant" de type BaseSimple
     * (mais rien ne nous empêche de la transmettre à une fonction qui s'attend à recevoir un ISimple,  un IRiche ou un IBiduloide)
     */
    class Riche : public BaseSimple, public IBiduloide, public IRiche{
     
    };
    /* SOLUTION N°2 : on veut la mélanger avec des objets "connus comme étant" de type BaseRiche
     * (mais rien ne nous empêche de la transmettre à une fonction qui s'attend à recevoir un ISimple,  un IRiche ou un IBiduloide)
     */
    class Riche : public BaseRiche, public IBiduloide{
     
    };
    /* SOLUTION N°3 : on veut la mélanger avec des objets "connus comme étant" de type Simple
     * (mais rien ne nous empêche de la transmettre à une fonction qui s'attend à recevoir un ISimple,  un IRiche ou un IBiduloide)
     */
    class Riche : public Simple, public IRiche{
     
    };
    /* SOLUTION N°4 : on veut uniquement la mélanger avec des objets "connus comme éant" du type MoinsSimple
     * (mais rien ne nous empêche de la transmettre à une fonction qui s'attend à recevoir un ISimple,  un IRiche ou un IBiduloide)
     */
     
     */
    class Riche : public MoinsSimple, public IRiche{
     
    };
    En définitive, la seule question qu'il faudra te poser (et à laquelle il s'agira de donner une réponse) sera : "sous quelle forme (de "parent le plus dérivé") est-ce que je veux que mes éléments de types Riche soient connus dans le meilleur des cas ", étant donné que, quoi qu'il advienne, il pourrons toujours être connus comme étant de type BaseSimple, et il sera toujours possible de les transmettre à une fonction s'attendant à recevoir n'importe quelle partie de son interface
    [EDIT]
    Maintenant, si, pour une raison ou une autre, tu ne sais pas modifier tes classes de base (par exemple, parce qu'elles sont fournies par une bibliothèque tierce), il faut garder en mémoire que la préférence sera toujours donnée à la composition et non à l'héritage (il faut déjà, pour que l'héritage soit cohérent, que LSP soit scrupuleusement respecté, et je subodore très fortement que ce n'est pas le cas dans ton cas de figure )
    Tu pourrais donc très bien avoir quelque chose comme (je reprends ici les termes de ton exemple d'origine)
    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 Riche : public MoinsSimple{
    public:
        /* on "remonte" le service manquant issu de BaseRiche */
       void riche(){
           riche_.riche();
       }
       /* et, pourquoi pas?, on peut permettre la récupération de la composante BaseRiche */
       BaseRiche /*const */ & toBaseRiche() /* const{
           return riche_;
       }
    private:
        BaseRiche riche_;
     
    }
    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

  8. #8
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Concrètement, mon contexte fait que je peux faire ce que je veux, mais que j'ai explicitement besoin de:
    • une interface telle que ISimple
    • une interface telle que ISimple + IRiche
    • une implémentation générale de chacune, plus une implémentation spécifique de ISimple (pattern composite inside)


    En effet, je recrée une partie d'une bibliothèque.
    Parmi ce que je dois garder, il y a une fonction devant prendre en argument un "EventHandler", ce "ISimple + IRiche".
    Le besoin métier est de pouvoir construire des ISimples basiques, ou composés de callbacks (des function<...>), et d'avoir un Riche qui propage des événements.

    Ma conclusion du moment:
    Après coup, je me dis qu'il n'est pas nécessaire que la fonction simple() de ISimple+IRiche porte le même nom que simple() de ISimple.
    Le EventHandler pourrait n'être que composé d'un ou plusieurs IRiche.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par leternel Voir le message
    Concrètement, mon contexte fait que je peux faire ce que je veux, mais que j'ai explicitement besoin de:
    • une interface telle que ISimple
    • une interface telle que ISimple + IRiche
    • une implémentation générale de chacune, plus une implémentation spécifique de ISimple (pattern composite inside)
    Ben, en C++, il n'y a absolument rien qui t'empêche d'avoir une implémentation "par défaut" pour tes interfaces... Tu peux d'ailleurs très bien fournir des données membres pour celles-ci, si certaines conditions sont respectées (principalement : le fait que, quoi qu'il arrive, ces données membres seront toujours utilisées pour les différentes dérivations possibles )

    En effet, je recrée une partie d'une bibliothèque.
    Parmi ce que je dois garder, il y a une fonction devant prendre en argument un "EventHandler", ce "ISimple + IRiche".
    Le besoin métier est de pouvoir construire des ISimples basiques, ou composés de callbacks (des function<...>), et d'avoir un Riche qui propage des événements.
    Pourquoi ne passerais tu pas par un système de signaux et de slots? Après tout, les callbacks ne sont jamais qu'un moyen d'assurer une transmission "horizontale" des informations, et les signaux + slots correspondent effectivement à ce cas de figure
    Ma conclusion du moment:
    Après coup, je me dis qu'il n'est pas nécessaire que la fonction simple() de ISimple+IRiche porte le même nom que simple() de ISimple.e EventHandler pourrait n'être que composé d'un ou plusieurs IRiche.
    Ce qui plaide en faveur de la composition par rapport à l'héritage, même si, d'une manière générale, il sera sans doute utile d'avoir "quelque part" une hiérarchie regroupant ISimple et IRiche

    Si ce n'est cette hiérarchie ne devrait -- à mon sens -- pas se faire au niveau des interface (IRiche héritant de ISimple), mais bien "parallèlement" à tes interfaces (dans le pire des cas, une classe de base dont hérite une classe utilisant la composition de ISimple, une autre utilisant la composition de IRiche et une dernière utilisant la composition des deux)
    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

  10. #10
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Je note toutes ces remarques, je mets tout ca en place et je ferai un retour.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Héritage virtuel et constructeur.
    Par Klaim dans le forum C++
    Réponses: 17
    Dernier message: 10/01/2015, 21h18
  2. Interfaces VS héritage
    Par FRED.G dans le forum Général Dotnet
    Réponses: 38
    Dernier message: 17/03/2007, 09h32
  3. [POO] Interface ou héritage ?
    Par s.n.a.f.u dans le forum VB.NET
    Réponses: 3
    Dernier message: 17/03/2007, 01h02
  4. Héritage Virtuel et SDL
    Par Qualimero dans le forum SDL
    Réponses: 6
    Dernier message: 18/07/2006, 04h49
  5. Interface et héritage
    Par pirbd dans le forum Delphi
    Réponses: 2
    Dernier message: 12/07/2006, 13h40

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