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 :

Pointeur de pointeur/ Référence sur un pointeur


Sujet :

C++

  1. #1
    Membre régulier
    Homme Profil pro
    Étudiant
    Inscrit en
    Septembre 2011
    Messages
    144
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2011
    Messages : 144
    Points : 118
    Points
    118
    Par défaut Pointeur de pointeur/ Référence sur un pointeur
    Bonjour,

    Je créé une petite application en C++ pour apprendre et je suis arrivé sur un cas que je ne suis pas sûr de maîtriser.

    Dans mon main :
    - j'instancie un pointeur Application Application app = new Application(); .
    - j'instancie un pointeur Display en passant en paramètre le pointeur app Display display= new Display (app); .
    - j'affecte le display à un attribut de la classe Application via un setter app.setDisplay(display); .


    Jusqu'ici, aucun problème. Cependant, dans ma classe Application, j'ai un attribut "Manager" qui est instancié dans la liste d'initialisation de mon constructeur Application.
    Le problème, c'est que Manager a besoin d'utiliser le Display, et au moment où il est instancié (la liste d'initialisation de mon constructeur Application), le pointeur display est NULL (ce qui est normal à ce moment là).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
        Application::Application() : manager()
        {
            this->display = NULL;
        }

    Est-ce qu'il est possible (et surtout, est-ce la bonne solution) de passer un pointeur de pointeur/une référence sur un pointeur ou un truc dans le genre afin que le Manager puisse utiliser le Display affecté à Application, même après un changement de valeur ?
    Je pourrais faire en sorte que le setDisplay de Application appelle un setDisplay dans le Manager, mais ce serait lourd car j'ai potentiellement plusieurs Managers (qui eux aussi auront d'autres Managers ^^).

    De même, est-ce qu'il est possible d'empêcher le Manager de modifier l'adresse du pointeur ? C'est à dire :
    >>> le manager doit pouvoir utiliser complètement le display et toutes ses méthodes sans restrictions, y compris des méthodes qui modifient des valeurs/attributs de la classe Display, etc.
    >>> la seule chose qu'il ne doit pas pouvoir faire c'est de modifier l'adresse sur laquelle pointe le pointeur (je ne sais pas si je m'exprime bien, mais en gros, il ne faut pas pouvoir faire un truc du genre display = new Display() dans le manager).


    Merci d'avance

  2. #2
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Dans un premier temps, je réfléchirais si tu as vraiment besoin de passer par des pointeurs. Si il le faut, utilise des shared_ptr<T> ou unique_ptr<T>, l'usage est très simple et te simplifiera la vie pour ton problème :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Application::Application(const std::shared_ptr<Display> &display)
    : manager()
    , display(display)
    {}
    Voir la doc ici.

  3. #3
    Membre éprouvé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    345
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Moselle (Lorraine)

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

    Informations forums :
    Inscription : Juin 2014
    Messages : 345
    Points : 1 211
    Points
    1 211
    Par défaut
    Bonjour,

    Un truc comme ça ?

    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
    class Manager {
        // ...
        Display* const & _display;
      public:
        Manager(Display* const & d):_display(d) { /* ... */ }
        // ...
    };
     
    class Application {
        // ...
        Display* _display;
        Manager _manager;
      public:
        Application():_display(0), _manager(_d) { /* ... */ }
        // ...
    };
    Manager dispose d'une référence sur pointeur constant sur Display, ce qui veut dire que, si app est une instance de Application et manager une instance de Manager :
    - si app._display change, manager._display change aussi, puisque ce dernier est une référence sur le premier
    - on ne peut pas réassigner manager._display, le pointeur étant constant
    - on peut réassigner app._display, le pointeur n'étant pas constant
    - l'objet pointé n'est pas constant, donc *app._display peut appeler des fonction non-const, et *manager._display aussi.

    Cela dit, lorsque le constructeur de Manager est appelé, l'objet Application est en cours de construction et je ne sais pas s'il est valide de passer une référence sur un membre d'un objet en construction à un constructeur. Il faudrait voir ce que le standard dit à ce propos.

  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
    Salut

    Conseiller l'usage de smart pointers n'est pas en soi une mauvaise idée, en revanche, ça ne règle pas le problème de design soulevé. Face à un problème de ce genre, la question à se poser serait : est-il normal que de nombreux managers aient un accès direct au display ? Le SRP est-il respecté ?

    Si oui, tu fais face à un problème que tu peux régler en rajoutant un niveau d'indirection. C'est à dire, plutôt que de fournir une référence à un display aux managers (j'utilise le mot référence au sens général du terme, sans s'intéresser à comment tu références, via un pointeur, smart pointer ou autre), tu vas fournir une référence vers un objet hub qui lui portera la référence vers le display et l'expose publiquement via un accesseur. Lorsque tu changeras le display, tu ne devras que changer la référence du hub, et tous les managers retomberont naturellement sur leurs pattes sans qu'il y ai besoin de tous les mettre à jour. Je te déconseille pour cela d'user d'un singleton. Mieux vaut un hub dont la durée de vie est liée à l'application.

    Citation Envoyé par the Hound Voir le message
    Manager dispose d'une référence sur pointeur constant sur Display, ce qui veut dire que, si app est une instance de Application et manager une instance de Manager :
    - si app._display change, manager._display change aussi, puisque ce dernier est une référence sur le premier
    - on ne peut pas réassigner manager._display, le pointeur étant constant
    - on peut réassigner app._display, le pointeur n'étant pas constant
    - l'objet pointé n'est pas constant, donc *app._display peut appeler des fonction non-const, et *manager._display aussi.
    Beaucoup d'erreurs là dedans !
    - Premièrement, const s'applique à gauche. C'est donc l'objet pointé qui est constant, et pas la référence. La notation avec const tout à gauche est très courante mais néanmoins abusive, bien que tolérée par la standard.
    - On ne peut réassigner aucune référence, constante ou pas. On pourrait réassigner l'objet qu'elle pointe, s'il implémente l'opérateur d'affectation, ce qui serait très douteux puisqu'on a une sémantique d'entité ici.
    - app._display ne peut pas changer pour pointer vers un nouvel objet. Pour pouvoir faire ça, il faut utiliser un pointeur, un smart pointer ou un reference_wrapper. Du reste, même si app._display change, ça n'aura aucun effet sur manager._display.

    Si tu avais testé tes affirmations sur du code réel, même de test, tu aurais pu constater par toi même la plupart de ces erreurs.


    Ok j'avais pas vu que c'était une référence sur pointeur. Confusing man.

    Citation Envoyé par the Hound Voir le message
    Cela dit, lorsque le constructeur de Manager est appelé, l'objet Application est en cours de construction et je ne sais pas s'il est valide de passer une référence sur un membre d'un objet en construction à un constructeur.
    Remarque pertinente, la FAQ traite ce problème.
    Find me on github

  5. #5
    Membre éprouvé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    345
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Moselle (Lorraine)

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

    Informations forums :
    Inscription : Juin 2014
    Messages : 345
    Points : 1 211
    Points
    1 211
    Par défaut
    Citation Envoyé par jblecanard Voir le message
    Beaucoup d'erreurs grossières là dedans !
    - Premièrement, const s'applique à gauche. C'est donc l'objet pointé qui est constant, et pas la référence. La notation avec const tout à gauche est très courante mais néanmoins abusive, bien que tolérée par la standard.
    - On ne peut réassigner aucune référence, constante ou pas. On pourrait réassigner l'objet qu'elle pointe, s'il implémente l'opérateur d'affectation, ce qui serait très douteux puisqu'on a une sémantique d'entité ici.
    - app._display ne peut pas changer pour pointer vers un nouvel objet. Pour pouvoir faire ça, il faut utiliser un pointeur, un smart pointer ou un reference_wrapper. Du reste, même si app._display change, ça n'aura aucun effet sur manager._display.

    Si tu avais testé tes affirmations sur du code réel, même de test, tu aurais pu constater par toi même la plupart de ces erreurs.
    Je vais répondre dans l'ordre :
    - Oui, const s'applique à gauche. Dans le cas de Display* const &, c'est bien Display* qui est constant, et c'est le pointeur sur l'objet concerné, pas l'objet en lui-même.
    - On ne peut pas réassigner de référence, et c'est pour ça qu'il est absurde de déclarer une référence constante. A quel moment je réassigne une référence ?
    - app._display est de type Display*, c'est donc un pointeur natif, et rien n'empêche de réassigner des pointeurs.
    - J'ai testé le code et il fonctionne.

    Sinon, comme tu le dis, il y a là un problème de design, mais la solution que tu proposes n'est en soi pas une solution. En effet, admettons qu'on implémente une classe Hub qui n'est pas destinée à fournir un singleton : il faudra instancier cette classe au moins une fois, et passer l'instance de cette classe à tous les Manager. Or c'est précisément là le problème : comment passer un objet à plein d'instances de Manager de manière efficace ? Qu'on passe un objet de type Hub ou Display, c'est le même problème. Tu remarqueras d'ailleurs que si, sémantiquement, Application est "faite" pour contenir un objet de type Display, alors la classe Application correspond à peu près à la classe Hub que tu proposes. Remarque par ailleurs, le fait de passer des références (en tous genre) de Display à des instances d'une autre classe ne viole pas le SRP, puisque cela n'empêche pas les différentes classes de traiter les tâches pour lesquelles elles sont faites. Quand une fonction membre de Manager appelle _display->toto(), c'est bien la classe Display qui encapsule cette action, et non pas Manager.

    PS: d'après ce que j'ai compris, on peut passer app._display au constructeur de Manager, tant que app._display est initialisée avant. Donc le code que j'ai soumis est valide, en théorie.

  6. #6
    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
    Citation Envoyé par the Hound Voir le message
    Je vais répondre dans l'ordre [...]
    Au temps pour moi, je n'avais pas vu que tu proposais une référence sur pointeur ! Honnêtement, c'est pas une bonne idée, c'est une source de confusion.

    Citation Envoyé par the Hound Voir le message
    Sinon, comme tu le dis, il y a là un problème de design, mais la solution que tu proposes n'est en soi pas une solution. En effet, admettons qu'on implémente une classe Hub qui n'est pas destinée à fournir un singleton : il faudra instancier cette classe au moins une fois, et passer l'instance de cette classe à tous les Manager. Or c'est précisément là le problème : comment passer un objet à "plein d'" instances de Manager de manière efficace ? Qu'on passe un objet de type Hub ou Display, c'est le même problème. Tu remarqueras d'ailleurs que si, sémantiquement, Application est "faite" pour contenir un objet de type Display, alors la classe Application correspond à peu près à la classe Hub que tu proposes. Remarque par ailleurs, le fait de passer des références (en tous genre) de Display à des instances d'une autre classe ne viole pas le SRP, puisque cela n'empêche pas les différentes classes de traiter les tâches pour lesquelles elles sont faites. Quand une fonction membre de Manager appelle _display->toto(), c'est bien la classe Display qui encapsule cette action, et non pas Manager.
    C'est une solution, mais pas une solution pour paresseux. Si tu utilises un singleton, tu ne pourras jamais instancier deux fois "Application" dans un seul exécutable. Par quoi se justifie une telle limitation ? Oui il faut se trimbaler la référence au hub partout. C'est à ça que sert le hub, donner l'accès aux objets élus à d'autre objets plus élevés dans la hiérarchie. Et oui, Application pourrait être ce hub, mais je le déconseille d'expérience car tu rajoutes un couplage supplémentaire, et tu exposes dans les managers une interface dont ils n'ont pas besoin, voir l'ISP à ce sujet. Tu peux aussi faire dériver l'application d'une classe abstraite et ne garder que cette interface dans les managers, ça marche aussi. Au final, la solution du hub est presque la même que celle du pointeur de pointeur (ou de la référence sur pointer, ça revient au même), à ceci prêt que tu n'es pas limité au display et que tu peux mettre d'autres objets intéressants dans le hub (la racine du modèle par exemple).

    Il n'y a pas qu'une seule manière de s'y prendre, l'important ça reste de bien découpler dès le début sinon c'est la galère après pour réparer.
    Find me on github

  7. #7
    Membre éprouvé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    345
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Moselle (Lorraine)

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

    Informations forums :
    Inscription : Juin 2014
    Messages : 345
    Points : 1 211
    Points
    1 211
    Par défaut
    Les références sur pointeurs, c'est effectivement une solution à éviter, mais ici je ne faisais que fournir une implémentation de la solution envisagée par gilloddon.

    D'autre part, ce que tu dis est juste, mais ne répond pas à la question. Il serait effectivement sûrement plus efficace de fournir une classe intermédiaire pour gérer les instances devant être partagées par différents acteurs dans le programme, mais il faudrait avoir une vision globale de l'architecture du projet pour en être sûrs. Bref, dans tous les cas, le problème était de pouvoir partager un objet tout en ayant des contraintes sur ce dernier, et en fonction des classes ; si une solution conceptuelle est préférable, des workarounds techniques sont possibles, et c'est ce que je propose via mon implémentation, même si oui, d'un point de vue concept, c'est loin d'être le mieux.

  8. #8
    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
    Globalement on propose la même chose, à part qu'au lieu de pointer un pointeur, je pointe un objet qui contient un pointeur. C'est plus propret, mais les défauts principaux restent les mêmes oui.
    Find me on github

Discussions similaires

  1. Réponses: 7
    Dernier message: 25/07/2013, 12h02
  2. Réponses: 11
    Dernier message: 14/05/2009, 14h07
  3. Dev c++ en rade sur les pointeurs ?...
    Par Magicien d'Oz dans le forum C
    Réponses: 17
    Dernier message: 13/10/2005, 19h38
  4. questions sur les pointeurs
    Par Hyoga dans le forum C++
    Réponses: 17
    Dernier message: 08/01/2005, 23h25
  5. Pb de débutant sur les pointeurs!!!
    Par benji17c dans le forum C
    Réponses: 6
    Dernier message: 30/09/2003, 17h50

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