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 :

Hériter la même fonction (de même signature) de deux classes


Sujet :

C++

  1. #1
    Membre régulier
    Inscrit en
    Septembre 2007
    Messages
    267
    Détails du profil
    Informations personnelles :
    Âge : 37

    Informations forums :
    Inscription : Septembre 2007
    Messages : 267
    Points : 119
    Points
    119
    Par défaut Hériter la même fonction (de même signature) de deux classes
    Salut,

    Je me demande pourquoi ce code génère une erreur "request for member 'f' is ambiguous" à la compilation :

    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
     
    class A
    {
    private:
        void f(){}
    };
     
    class B
    {
    public:
        void f(){}
    };
     
    class D: public A, public B
    {
    };
     
    int main()
    {
        D d;
        d.f();
    }
    ...vu que f() de A est private, la classe D ne devrait pas avoir de visibilité dessus.
    J'aurais pensé que f() de A n'existe pas pour la classe D.
    Rassurez-vous j'ai cherché sur le web (pas trop longtemps non plus), et j'n'ai pas trouvé d'exemple parlant de ce cas.

    NB : je compile avec Qt Creator sur windows. J'ai configuré en C++11 et j'ai exactement ces messages :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    ..\exemple\main.cpp: In function 'int main()':
    ..\exemple\main.cpp:22:7: error: request for member 'f' is ambiguous
         d.f();
           ^
    ..\exemple\main.cpp:12:10: note: candidates are: void B::f()
         void f(){}
              ^
    ..\exemple\main.cpp:6:10: note:                 void A::f()
         void f(){}

  2. #2
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut
    Bonjour,

    En fait, il y a une collision de nommage, c'est-à-dire que, comme les classes A et B ont chacune une fonction-membre appelée f et que la classe D hérite des ces deux classes, le compilateur ne sait pas de quelle fonction f tu parles; aucun lien avec la visibilité des dites fonctions-membres, donc. Pour régler ce problème, il faut que tu imposes un choix au compilateur en utilisant le mot-clef using. 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
    class A
    {
      private:
        void f(){}
    };
     
    class B
    {
      public:
        void f(){}
    };
     
    class D:
      public A,
      public B
    {
      public:
        using B::f;
    };
     
    int main()
    {
      D d;
      d.f();
    }
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

  3. #3
    Membre régulier
    Inscrit en
    Septembre 2007
    Messages
    267
    Détails du profil
    Informations personnelles :
    Âge : 37

    Informations forums :
    Inscription : Septembre 2007
    Messages : 267
    Points : 119
    Points
    119
    Par défaut
    je ne trouve pas ca logique de la part du compilo vu qu'on a pas le droit d'écrire "using A::f".

    En fait il s'emmerde pas à ce niveau le compilo quoi ^^
    Enfin bon je ne trouve pas ca logique tel qu'on m'a expliqué et de ce que j'ai compris de l'utilisation des mots clé private, protected et public, mais peut être que ca vous semble logique à vous. Si oui, veuillez m'expliquer svp.

    EDIT :
    C'est peut etre aussi un choix du compilo, pour pas que le développeur se goure, au cas où dans un code lourd, le développeur pense utiliser A::f ayant oublié l’existence de B::f et du mot clé private, donc il va soulever l'erreur à tous les coups, comme ca, pas d’ambiguïté.

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    Cela ne vous parait pas logique car vous n'avez pas la bonne démarche objet.

    Chaque objet est un fournisseur de service, et ont ne doit pas connaitre son pédigrée sur 10 générations pour pouvoir s'en servir.

    On ne doit pas résonner l'héritage en terme de récupération de code mais uniquement en terme de "Un objet de la classe Fille est aussi un objet de la classe mère".

    Donc, le compilateur qui doit construire une classe "D", il doit savoir comment interpréter un appel à la méthode "f".
    Il fait un peu de tambouille avec le nom de la fonction et les types de ces paramètres, formant la signature de la méthode, pour avoir un nom décoré avec des ?@# dans tous les sens, et vérifie s'il existe une fonction avec le même nom décoré dans les méthodes de ces classes parentes.
    Dans le nom décoré, il n'y a pas le scope privé/protected/public, car ont peut restreindre le scope d'une méthode dans une classe dérivée.

    Le compilateur se retrouve donc avec 2 méthodes avec le même nom décoré.
    Là, faut lui expliquer comment choisir la méthode "f", à coup de using.

    Cela ne vous semble pas naturel, c'est pas grave, de toute façon des noms de méthodes identiques pour des services (ici les services de A et de B) on évite.

  5. #5
    Membre régulier
    Inscrit en
    Septembre 2007
    Messages
    267
    Détails du profil
    Informations personnelles :
    Âge : 37

    Informations forums :
    Inscription : Septembre 2007
    Messages : 267
    Points : 119
    Points
    119
    Par défaut
    okay, I understand now. J'ai marqué résolu.

    Ceci dit j'ai d'autres questions:

    - Est-ce que dans le cas d'une fonction private chez le parent, l'enfant génère "un nom décoré avec des ?@# dans tous les sens" mais inaccessible par une instance de la classe fille ?
    - "car ont peut restreindre le scope d'une méthode dans une classe dérivée" mais comment fait-on déjà (dans un cas pratique) ?

    Thanks in advance.

  6. #6
    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,

    Il n'y a rien au niveau du langage qui t'interdise de "changer" l'accessibilité d'une fonction virtuelle lorsque la redéfini au niveau d'une classe dérivée; que ce soit pour "lever des interdictions" en passant de privé à protégé, par exemple) ou en restreignant d'avantage l'accès (en passant de protégé à privé, par exemple).

    Cependant, du point de vue purement conceptuel, l'augmentation des restrictions (passage de l'accessibilité publique à une accessibilité plus réduite) risque très fort de contrevenir au LSP et il faut vraiment avoir de très bonnes raisons pour faire passer une fonction privée (ou protégée) de la classe mère dans l'accessibilité protégée (ou publique) de la classe fille. (c'est, une fois encore, le coup du "on peut le faire, mais on n'a sans doute pas intérêt à le faire )

    De plus, il faut savoir que la notion d'accessibilité est une notion qui n'apparait qu'au niveau du compilateur : une fois qu'il a fini son job, il n'y a plus aucune différence entre une fonction privée, une fonction protégée et une fonction publique : les trois sont considérées comme des fonctions membre, point-barre.

    Le truc, c'est qu'une fonction membre privée ne pourra être appelée que par les fonctions membres de la même classe ainsi que par les fonctions déclarées amies (ou les fonctions membre de classes déclarées amies), qu'une fonction membre protégée pourra être appelées par les fonctions membres de la classe ainsi que part "toute fonction membre issue d'une classe dérivée" (en plus des fonctions amies et des fonctions membres de classes amies) et qu'une fonction membre publique pourra être appelée depuis "n'importe où dans le code".
    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

  7. #7
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut
    Bonjour,

    Comme le dit koala01, changer la visibilité des membres est tout à fait possible mais il faut prendre certaines précautions.

    Par exemple, admettons qu'on ait une classe C1 avec une fonction-membre publique f ainsi qu'une classe C2 héritant de C1 mais dont la fonction-membre héritée f n'est plus publique mais privée. Admettons maintenant qu'on gère une instance de C2 au travers d'un pointeur p du type C1 *. On pourra accéder à la fonction-membre f de *p partout dans le code alors qu'elle est sensée être privée, juste parce qu'elle est publique chez C1. Naturellement, ce genre de choses peut mener des comportements inattendus et donc générer de vilains bugs, du moins dans les meilleurs cas de figure.
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

  8. #8
    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
    Attention, je le répète, faire passer une fonction de l'accessibilité publique dans la classe mère à une accessibilité privée dans la classe dérivée contrevient au principe de substitution de liskov, et ne devrait donc jamais être tenté car CONCEPTUELLEMENT INCORRECT.

    Quant à l'idée qui nous inciterait à faire passer une fonction privée (ou protégée) dans la classe de base vers l'accessibilité publique dans la classe dérivée, j'ai envie de dire qu'il n'y a rien qui nous l'interdise du point de vue conceptuel. Mais nous aurions alors très largement intérêt à se poser la question de savoir pourquoi le développeur de la classe mère aura décidé de restreindre l'accès à la fonction en question et, partant, de l'intérêt que nous nous pourrions avoir à la faire passer dans une accessibilité "plus permissive".

    Et, de manière générale, on devrait quasi systématiquement en arriver à la conclusion que le développeur de la classe mère avait de très bonnes raisons de placer cette fonction dans l'accessibilité restreinte qu'il a choisie; et que ces raisons se retrouvent également au niveau de la classe dérivée
    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

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

Discussions similaires

  1. lancer deux fonctions en même temps
    Par youp_db dans le forum Général JavaScript
    Réponses: 2
    Dernier message: 28/09/2006, 12h11
  2. Réponses: 3
    Dernier message: 11/07/2006, 17h45
  3. Réponses: 4
    Dernier message: 07/03/2006, 15h54
  4. TForm pour plusieurs fenêtre dans une même fonction ?
    Par MaTHieU_ dans le forum C++Builder
    Réponses: 5
    Dernier message: 15/11/2005, 12h38
  5. [VB.NET] Appliquer plusieurs fois la même fonction...
    Par MiJack dans le forum Windows Forms
    Réponses: 9
    Dernier message: 22/09/2004, 10h52

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