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 :

Déréferencement des méthodes virtuelles


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    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
    Par défaut Déréferencement des méthodes virtuelles
    Bonjour

    en codant aujourd hui, je constate un comportement que je connaissais pas. Mettons que j'aie deux classes, une classe "Mere" et "Enfant" possédant toute deux une méthode virtuelle "methode()". Enfant hérite de Mere, sa méthode est donc censée remplacer celle de Mere.

    Dans le code suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Mere objet = Enfant();
    objet.methode();
    C'est le code de la méthode de Mere qui est appelé. En revanche avec le code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Mere* objet = new Enfant();
    objet->methode();
    C'est bien le code de la méthode "methode()" de Enfant qui est appelé (ce qui le comportement que je recherche).

    Ma question est : est-ce normal ? Si oui pourquoi ? N'est ce pas bête de restreindre le polymorphisme aux pointeurs ?

  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,

    Le polymorphisme s'applique sur les pointer et ou sur les références vers des objets existants.

    Quelque part, c'est finalement logique: Le polymorphisme va s'exprimer pleinement lorsque l'on a décidé de passer un objet dérivé comme argument à une fonction qui prend... un objet de base, ou lorsque l'on décide de considérer un objet de type dérivé renvoyé par une fonction comme étant un objet de base

    Lorsque l'on dispose du type réel de l'objet, il n'y a pas vraiment lieu de passer par le polymorphisme

    Les trois cas auxquels nous seront confrontés seront donc proches de
    1. void fonctionPrenantReference( Base /* const */ & param);
    2. void fonctionPrenantPointeur(Base * param);
    3. Base *ptr= fonctionRenvoyantPointeur();
    en prenant en compte le fait que le pointeur renvoyé par la troisième sera en fait un pointeur sur... l'objet de base

    Par contre, un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Base obj=Fille();
    obj.foo();
    n'a pas énormément de sens, du seul fait que, si tu veux déclarer une variable de type fille, il est largement préférable d'indiquer... qu'il s'agit bel et bien d'un type Fille:
    Fille obj;
    obj.foo()
    est, en effet, bien plus logique

    Par contre, si tu veux disposer d'une variable (temporaire) que tu veux faire passer pour un objet du type de base de manière à manipuler deux variables existantes différentes, tu ne voudra pas forcément provoquer la copie de ton objet d'origine, et tu passera donc, selon le cas, par une référence ou par un pointeur (mais est-ce logiquement intéressant de le faire ):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /* création d'un objet de base */
    Base b;
    /* Creation d'un objet dérivé */
    Derivee d;
    /* Est-ce réellement logique d'agir ainsi ?? */
    Base* ptr=&b;
    ptr->foo();
    ptr = d;
    d->foo();
    /* ou pire, ainsi */
    Base & ref1=b;
    ref1.foo();
    Base & ref2=d;
    ref2.foo();
    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 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
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Par contre, un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Base obj=Fille();
    obj.foo();
    n'a pas énormément de sens, du seul fait que, si tu veux déclarer une variable de type fille, il est largement préférable d'indiquer... qu'il s'agit bel et bien d'un type Fille:
    [/CODE]
    Justement, je ne suis pas tout à fait d'accord. Le code pourrait être :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Base obj= FactoryQuiRenvoieUnTypeBase();
    obj.foo();
    La factory, en fonction d'un contexte X ou Y, pourrait renvoyer une instance d'une classe fille pour modifier le comportement suivant de l'objet, et il n'y a pas de raison à priori pour que je ne veuille pas une copie.

    Après si on se place du point de vue compilateur (ce à quoi je n'ai pas pensé avant de poster), ça se comprend mieux puisque qu'il ne peut pas copier un objet hérité dans un conteneur à priori plus petit (base), au même titre qu'on ne peut mettre deux litres d'eau dans une gourde qui n'en contient qu'un. Problème évidemment inexistant avec des pointeurs ou des références.

  4. #4
    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
    Bien souvent, il est préférable que les factories utilisent l'allocation dynamique pour créer les objets qu'on leur demande (même si c'est basé sur un clonage )

    En effet, nous ne pouvons pas nous permettre de renvoyer une référence (du type de base) sur un objet créé sur la pile comme
    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
    /* Attention ce code est dangereux !!! */
    Base& createBase(bool de)
    {
        if(de)
        {
            Derivee d;
            return d;
        }
        Base b;
        return b;
    } 
    int main()
    {
        Base & item=createBase(true);
        /*...*/
    }
    parce que la référence renvoyée fait référence à un objet qui sera détruit lorsque l'on quittera la portée dans laquelle il est déclaré.

    En outre, nous ne pouvons pas écrire un code 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
    /* ce code serait valide */
    Base createBase(bool de)
    {
        if(de)
        {
            Derivee d;
            return d;
        }
        Base b;
        return b;
    } 
    /* mais nous perdons le polymorphisme */
    int main()
    {
        Base item=createBase(true);
        /* et ceci serait refusé */
        Base & ref=createBase(true);
    }
    Pour disposer du polymorphisme, du fait des restrictions le concernant, la seule solution raisonnable est 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
    Base createBase(bool de)
    {
        Base ptr;
        if(de)
            ptr=new Derivee;
        else
            ptr=new Base;
        return ptr;
    }
    int main()
    {
        Base myptr=createBase(true);
        /*...*/
        delete myptr;
    }
    qui se rapproche en définitive du troisième cas que j'ai cité plus haut
    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

  5. #5
    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
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Bien souvent, il est préférable que les factories utilisent l'allocation dynamique pour créer les objets qu'on leur demande (même si c'est basé sur un clonage )

    En effet, nous ne pouvons pas nous permettre de renvoyer une référence (du type de base) sur un objet créé sur la pile comme parce que la référence renvoyée fait référence à un objet qui sera détruit lorsque l'on quittera la portée dans laquelle il est déclaré.
    Je suis totalement d'accord ! Je ne discute pas de la manière propre ou non de faire les choses, je me questionnais sur le pourquoi technique du comportement observé.

  6. #6
    Expert confirmé

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Par défaut
    Citation Envoyé par jblecanard Voir le message
    Dans le code suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Mere objet = Enfant();
    objet.methode();
    C'est le code de la méthode de Mere qui est appelé.
    Effectivement. objet a pour type statique Mere et pour type dynamique Mere. Pourquoi voudrais-tu appeler une autre version? Il n'y a à ce moment aucun objet de type Enfant.

    1/ construit un objet temporaire de type Enfant
    2/ construit un objet de type Mere avec le constructeur adequat (Mere(Mere const& vraisemblablement, Mere(Enfant const& si tu en as défini un).
    3/ détruit l'objet temporaire de type Enfant.
    4/ appelle methode() sur objet .

    Ma question est : est-ce normal ?
    Oui.

    N'est ce pas bête de restreindre le polymorphisme aux pointeurs ?
    1/ J'ai l'impression que tu n'as pas compris qu'une copie a eu lieu.
    2/ Pour avoir une surcharge résolue dynamiquement, il faut que le type statique puisse être différent du type dynamique. Ce qui n'est possible qu'avec des pointeurs ou des références.

  7. #7
    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
    Par défaut
    Citation Envoyé par Jean-Marc.Bourguet Voir le message
    1/ J'ai l'impression que tu n'as pas compris qu'une copie a eu lieu.
    Si si j'ai toujours su celà.

    Citation Envoyé par Jean-Marc.Bourguet Voir le message
    2/ Pour avoir une surcharge résolue dynamiquement, il faut que le type statique puisse être différent du type dynamique.
    Oui, et le fond de ma question était : pourquoi donc ? C'est peut être évident pour certains, mais pas pour moi qui débarque du Java (ou tout les objets sont de type dynamique, la notion de pointeur n'existant pas). J'ai marqué le topic résolu parce qu'après réflexions et réponses de Koala, j'ai compris.

  8. #8
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    Citation Envoyé par jblecanard Voir le message
    C'est peut être évident pour certains, mais pas pour moi qui débarque du Java (ou tout les objets sont de type dynamique, donc où tout est pointeur).
    J'ai corrigé pour toi.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

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

Discussions similaires

  1. Implémentation des méthodes virtuelles en c++
    Par nosferatu dans le forum Langage
    Réponses: 3
    Dernier message: 24/03/2011, 14h37
  2. optimiser l appel a des méthodes virtuelles
    Par Frifron dans le forum C++
    Réponses: 2
    Dernier message: 20/07/2009, 07h37
  3. Réponses: 12
    Dernier message: 19/08/2007, 19h26
  4. [C#] Méthode virtuelle
    Par jacma dans le forum Windows Forms
    Réponses: 4
    Dernier message: 07/11/2004, 08h18
  5. [Info]descriptif des méthode ?
    Par java_math dans le forum Eclipse Java
    Réponses: 1
    Dernier message: 01/06/2004, 08h36

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