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 :

protected : portée d'instance et non de classe ?


Sujet :

C++

  1. #1
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Points : 444
    Points
    444
    Par défaut protected : portée d'instance et non de classe ?
    Bonjour,

    je suis tombé sur un résultat assez surprenant en compilant le code suivant :

    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
     
    class A
    {
     
    public:
     
        virtual ~A() {}
        void activate(bool is_active) { activate_impl(is_active); }
     
    protected:
     
        virtual void activate_impl(bool is_active) { std::cout << "activate in A" << std::endl; }
     
    };
     
    class B : public A
    {
     
    public:
     
        B() : p_a(new A) {}
        virtual ~B() { delete p_a; }
     
    protected:
     
        virtual void activate_impl(bool is_active) { p_a->activate_impl(is_active); }
    };
    J'obtiens le message d'erreur suivant : cannot access protected member declared in class A. Testé avec Visual Studio 2008 et g++ 4.

    Je trouve cela étrange dans la mesure où B dérive bien de A, je pensais que, de la même manière que le mot clé private, la portée du mot clé protected était une portée de classe et non d'instance.

    Si quelqu'un a une explication, je suis preneur

    Merci d'avance.

  2. #2
    screetch
    Invité(e)
    Par défaut
    tu peux utiliser les méthodes protected sur toi même pas sur une autre instance.
    Ton B la, c'est déjà un A et en plus il possède un pointeur sur un autre A, je pense donc que tu n'as pas tout compris a l'héritage (peut-être tu as compris mais ce code me semble suspicieux), si tu as des cours il faudrait les revoir.

  3. #3
    Expert confirmé Avatar de fregolo52
    Homme Profil pro
    Développeur C
    Inscrit en
    Août 2004
    Messages
    2 364
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur C

    Informations forums :
    Inscription : Août 2004
    Messages : 2 364
    Points : 5 378
    Points
    5 378
    Par défaut
    je ne suis pas une lumière dans ce domaine, mais le mot clé virtual ne jouerait pas aussi ?

    Et vu que tu as rédéfini activate_impl dans B, celle de A n'est plus accessible. Tu as cassé l'héritage.

  4. #4
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Points : 444
    Points
    444
    Par défaut
    @ screetch :

    Ce code est juste une simplification permettant de reproduire le problème, dans la réalité j'ai un pattern Decorator, donc rien d'anormal à dériver de A et avoir un pointeur sur A ; dans le vrai code, A est abstraite et mon p_a est une autre classe dérivée, mais peu importe encore une fois ce code est là pour reproduire le problème et rien de plus.

    La méthode qui m'intéresse est mise en protected car je souhaite qu'elle soit utilisable par mes différents decorators, mais pas par l'exterieur de la lib.

    J'ai bien vu que les méthodes protected ne sont appelables que sur une même instance, ma question est pourquoi un comportement différent du mot clé private ?

    @fregolo52 :

    Le mot clé virtual est hors de cause, on peut reproduire le même problème en appelant une méthode protected non virtual de A depuis une méthode public de B. De plus redéfinir une méthode virtuelle dans une classe dérivée n'empêche pas l'accès à la méthode de la classe mère (sur la même instance), il suffit de préciser A:: avant l'appel de la méthode.

  5. #5
    screetch
    Invité(e)
    Par défaut
    ah dakodak je comprends (pardon de prendre pour des débutants mais ici c'est le cas le plus fréquent )
    le pattern a été "inventé" récemment, ce cas n'était sans doute pas prévu a l'origine
    de plus le mot-clé "friend" ne convient pas forcément (friend selectionne les classes qui vont avoir accès a la méthode, et ca bloque la création de nouveaux décorateurs)
    mais c'est le plus proche de ce dont tu as besoin

  6. #6
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Points : 444
    Points
    444
    Par défaut
    Citation Envoyé par screetch Voir le message
    ah dakodak je comprends (pardon de prendre pour des débutants mais ici c'est le cas le plus fréquent )
    Pas de souci

    le pattern a été "inventé" récemment, ce cas n'était sans doute pas prévu a l'origine
    de plus le mot-clé "friend" ne convient pas forcément (friend selectionne les classes qui vont avoir accès a la méthode, et ca bloque la création de nouveaux décorateurs)
    mais c'est le plus proche de ce dont tu as besoin
    En fait j'utilise le "friend" pour résoudre mon pb, mais avec une classe intermédiaire friend de la class A, et une méthode publique utilisée par les decorators, redirigeant vers la méthode protected. Cela me permet de contourner le blocage de la création de nouveaux decorators. Par contre je trouve ça un peu bricolo

  7. #7
    Membre confirmé
    Avatar de gb_68
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2006
    Messages
    232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2006
    Messages : 232
    Points : 546
    Points
    546
    Par défaut
    Bonjour,
    un autre contournement du problème peut être de créer un méthode static protected :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class A
    { 
    public:
        //[...]
     
    protected:
        static void call_activate_impl(A & a, bool is_active) 
        { a.activate_impl(is_active); }
        //[...]
    };
    Cette méthode est uniquement accessible depuis les classes dérivées et permet d’appeler la méthode interne voulue.
    Après je ne saurais dire si c'est plus propre/moins bricolage .

  8. #8
    Expert confirmé Avatar de fregolo52
    Homme Profil pro
    Développeur C
    Inscrit en
    Août 2004
    Messages
    2 364
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur C

    Informations forums :
    Inscription : Août 2004
    Messages : 2 364
    Points : 5 378
    Points
    5 378
    Par défaut
    Citation Envoyé par bolhrak Voir le message
    De plus redéfinir une méthode virtuelle dans une classe dérivée n'empêche pas l'accès à la méthode de la classe mère (sur la même instance), il suffit de préciser A:: avant l'appel de la méthode.
    pouhaaa !!!! faut que je dorme !!!! Je n'avais pas vu que tu appelais la méthode de A dans B.
    Désolé pour mon message à la con.

  9. #9
    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
    Salut,

    Si la logique entre activate et activate_impl, c'est le pattern NVI, les fonctions virtuelles devraient être privées et c'est bien l'objectif de forcer l'appel à la fonction non virtuelle publique de base et pas celle virtuelle y compris pour B avec une autre instance. Les mêmes raisons qui justifient le NVI vis à vis d'une classe externe les justifient vis à vis d'une autre instance de la même classe (elle agit en tant que client).

    Donc, il ne faut pas ajouter de l'amitié mais bien appeler la fonction de base non virtuelle :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    virtual void activate_impl(bool is_active) { p_a->activate(is_active);}
    Le pattern Decorator - presque plus vieux que le C++[*] - avec le NVI offre en outre la possibilité de bien différencier les aspects décorations des aspects spécialisations (ce qui me fait dire qu'il serait peut être intéressant de creuser ça en article) :
    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
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    #include <iostream>
    #include <boost\noncopyable.hpp>
     
    class Component : private boost::noncopyable
    {
    public:
        void operation()const
        {do_operation();}
     
     
        virtual ~Component(){}
     
    private:
        virtual void do_operation()const=0;
     
    };
     
    class ConcreteComponent : public Component
    {
    private:
        virtual void do_operation()const
        {
            std::cout<<"ConcreteComponent::do_operation\n";
        }
     
    };
     
     
    class Decorator : public Component
    {
    public:
        Decorator(Component &decorated_)
            :decorated(decorated_)
        {
        }
     
        virtual ~Decorator(){}
     
    protected:
        Component const&getComponent()const
        {
            return decorated;
        }
    private:
        void decorate()const
        {
            do_decorate();
        }
     
        virtual void do_operation()const // ça aurait du sens qu'elle soit 'final' - pour moi qui me suit souvent demandé l'intérêt de ce mot clé, je crois que j'en ai trouvé un :D
        {
            decorate();
        }
        virtual void do_decorate()const=0;
     
     
        Component &decorated;
    };
     
    class PreConcreteDecorator : public Decorator
    {
    public:
        PreConcreteDecorator(Component &decorated_)
            :Decorator(decorated_)
        {
        }
     
    private:
        virtual void do_decorate()const
        {
            std::cout<<"a pre decoration\n";
            getComponent().operation();
        }
    };
     
    class PostConcreteDecorator : public Decorator
    {
    public:
        PostConcreteDecorator(Component &decorated_)
            :Decorator(decorated_)
        {
        }
     
    private:
        virtual void do_decorate() const
        {
            getComponent().operation();
            std::cout<<"a post decorator\n";
        }
    };
     
     
    class NonChainingConcreteDecorator : public Decorator
    {
    public:
        NonChainingConcreteDecorator(Component &decorated_)
            :Decorator(decorated_)
        {
        }
     
    private:
        virtual void do_decorate() const
        {
            std::cout<<"a non chaining decorator\n";
        }
    };
     
     
    void call_it(Component &comp_)
    {
        comp_.operation();
    }
     
    int main()
    {
        ConcreteComponent cc;
        std::cout<<"**********\nConcreteComponent : \n";
        call_it(cc);
     
        PreConcreteDecorator  pre_cc(cc);
        std::cout<<"**********\nPreConcreteDecorator : \n";
        call_it(pre_cc);
     
        PostConcreteDecorator  post_cc(cc);
        std::cout<<"**********\nPostConcreteDecorator : \n";
        call_it(post_cc);
     
     
        PostConcreteDecorator  post_pre_cc(pre_cc);
        std::cout<<"**********\nPostConcreteDecorator(PreConcreteDecorator) : \n";
        call_it(post_pre_cc);
     
        NonChainingConcreteDecorator  nonchaining_cc(pre_cc);
        std::cout<<"**********\nNonChainingConcreteDecorator : \n";
        call_it(nonchaining_cc);
     
        NonChainingConcreteDecorator  nonchaining_prove_cc(post_pre_cc);
        std::cout<<"**********\nNonChainingConcreteDecorator(PostConcreteDecorator(PreConcreteDecorator)) : \n";
        call_it(nonchaining_cc);
     
        return 0;
    }
    [*]: GoF : 1994 / C++ 'inventé' en 83 mais normalisé en 98

  10. #10
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Points : 444
    Points
    444
    Par défaut
    En fait ma problématique n'est pas vraiment l'implémentation du pattern NVI (effectivement mon exemple simplifié est un peu trompeur sur cet aspect) ; je souhaite que cette méthode ne soit invocable depuis l'extérieur qu'en construisant un objet de scope, la méthode sera appelée avec true à la construction, avec false à la destruction, de sorte que l'état de mon objet est géré facilement. En interne, les decorateurs doivent pouvoir forwarder cet appel à la classe décorée.

    La méthode est donc protégée virtuelle dès le départ, je n'ai pas d'interface publique.

  11. #11
    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 bolhrak Voir le message
    En fait ma problématique n'est pas vraiment l'implémentation du pattern NVI (effectivement mon exemple simplifié est un peu trompeur sur cet aspect) ; je souhaite que cette méthode ne soit invocable qu'en construisant un objet de scope, la méthode sera appelée avec true à la construction, avec false à la destruction, de sorte que l'état de mon objet est géré facilement.

    La méthode est donc protégée virtuelle dès le départ, je n'ai pas d'interface publique.
    Derrière NVI, l'important est que la fonction virtuelle soit privée. Que la fonction non virtuelle soit publique ou protégée est "secondaire".

    Pour rappel, l'appel d'une fonction virtuelle dans un constructeur ou destructeur n'appelle pas la fonction de l'objet le plus dérivé mais celui du constructeur/destructeur en cours d'exécution (comportement indéterminé pour une fonction virtuelle pure dans une classe abstraite).

    D'un point de vue design, avoir un attribut à vrai à la construction et à faux à la destruction me laisse dubitatif (dit autrement, ça pue à 100m).

  12. #12
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Points : 444
    Points
    444
    Par défaut
    Arf j'ai vraiment dû mal m'exprimer. Cette fonction virtuelle n'est pas appelée par un constructeur ou un destructeur de la hiérarchie. Le code suivant devrait mieux illustrer mes propos (en tout cas c'est ce que je voulais faire, avant de rencontrer ce 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
    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
     
     
    class Interface
    {
     
    protected:
     
        virtual void activate_cache(bool is_active) =0;
     
        friend class ScopedInterfaceCache;
    };
     
    class Decorator : public Interface
    {
     
    protected:
     
        virtual void activate_cache(bool is_active) {
            p_a->activate_cache(_is_active);
        }
     
        Interface* p_a;
    };
     
    class ScopedInterfaceCache
    {
     
    public:
     
        ScopedInterfaceCache(Interface* in) : p_interface(in)
        {
            p_interface->activate_cache(true);
        }
     
        ~ScopedInterfaceCache()
        {
            p_interface->activate_cache(false);
        }
     
    private:
        Interface* p_interface;
    };
     
    // Utilisation :
    void fonction(Interface* in)
    {
    // ...
    // ...
        {
        ScopedInterfaceCache cache(in);
        // utilisation de in pour diverse opérations
        } // ici la méthode activate_cache est appelée par le destructeur  de cache
     
    }
    Voilà j'espère que c'est un peu plus parlant.

  13. #13
    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
    Ok. Je crois que j'ai compris. ScopedInterfaceCache est une enveloppe RAII sur Interface (comme un thread::unique_lock ou un std::shared_ptr) (d'ailleurs, tu devrais gérer sa politique de copie ... par une noncopiable probablement pour un scoped).

  14. #14
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Points : 444
    Points
    444
    Par défaut
    Exactement. Et effectivement j'en fais une classe non copiable.

Discussions similaires

  1. Variable d'instance et variable de classe
    Par glycerine dans le forum C++
    Réponses: 6
    Dernier message: 02/03/2015, 13h37
  2. Portée d'instance de classe.
    Par Mornor dans le forum Général Java
    Réponses: 12
    Dernier message: 09/12/2013, 22h33
  3. communication Port Usb en mode non bloquant
    Par laurentleroy dans le forum C
    Réponses: 4
    Dernier message: 28/10/2007, 23h29
  4. checker si un port est utilisé ou non
    Par Jérémy Lefevre dans le forum Flash
    Réponses: 7
    Dernier message: 06/06/2007, 14h23
  5. Réponses: 3
    Dernier message: 13/09/2006, 17h27

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