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 :

Référence en membre d'un classe


Sujet :

Langage C++

  1. #1
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut Référence en membre d'un classe
    Bonjour.

    Dans un projet, j'ai trouvé des classes utilisant des références pour ses membres
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class foo
    {
        bar& m_bar;
        public :
        foo(bar & b):m_bar(b)
        {
        }
    };
    Je serais curieux de savoir ce que vous en pensez et si il existe un réel points positif à utiliser cela.

    Personnellement, je n'en voie pas l'utilité,je trouve que cela permet plus de problème qu'autre chose et je les aurais replacés par des std::shared_ptr.

  2. #2
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Pourquoi utiliser un std::shared_ptr quand on à la garanti que la référence sera valide ?

    Un std::shared_ptr va rendre le code plus dur à relire vu qu'il sous entend que la possession de l'objet est partagée alors qu'elle ne l'est pas ici (et, accessoirement, ça peut avoir un impact sur les performances).

  3. #3
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    • Qu'es ce qui peux garantir que la référence sera toujours valide dans le temps? A par le développeur d'origine?
    • Pour le problème de possession partager, on pourrais utiliser un weak_ptr. Cela permettra au moins de savoir si l'objet existe avant de l'utiliser et être sur qu'il ne sera pas détruit pendant son utilisation.
    • Pourquoi un impact sur les performances?



    yan

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

    Informations professionnelles :
    Activité : aucun

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

    Je comprend ton questionnement, car il est vrai que l'on ne sait absolument pas comment sera géré l'objet référencé par le membre.

    Tu as tout à fait raison de dire que "si ca se trouve, la durée de vie de l'objet référencé sera plus petite que celle de l'objet référençant". Mais il est tout à fait possible de garantir par construction (par la conception même de ton projet) que ce ne sera pas le cas.

    A partir du moment où ta conception arrive à donner des garanties sur la durée de vie des différents éléments, cela ne me gène absolument pas de voir l'une des données membres être une référence, car la référence apporte une garantie de non nullité qu'aucun pointeur ne peut apporter.

    De plus, il est peut être bon de voir exactement le contexte dans lequel la classe est créée. Cela s'écarte peut être un peu du contexte de ton exemple, mais, imagine 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
    /* voici une classe qui est typiquement destinée à être utilisée dans une relation d'héritage */
    template <typename CRTP>
    class Interface{
    protected:
         Interface(CRTP /* const*/ &ref):derived_{refà}{}
         ~Interface() = default;
    private:
        CRTP /* const */ & derived_; /* permet de récupérer directement le bon objet
                                     * dans les fonction membres de Interface
                                     */
    };
    Dans ce genre de contexte, il n'y a aucun doute : la conception nous donne la garantie que l'objet référencé existera bel et bien tant que l'objet référençant existe. Où serait le besoin de recourir à un pointeur, qu'il soit intelligent ou non

    Du coup, même si j'avoue que cela fait un peu "réponse de normand", je dirais que... cela dépend. De plein de choses, et seul le projet peut nous permettre de juger si c'est "une bonne idée" ou non !!!
    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
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Citation Envoyé par koala01 Voir le message
    car la référence apporte une garantie de non nullité qu'aucun pointeur ne peut apporter.
    Entre un pointeur null et une référence sur un objet détruit... J'ai rien contre les références, au contraire, mais pour des fonctions.

    imagine un code proche de
    comment tu utilise cette classe??

    Citation Envoyé par koala01 Voir le message
    Du coup, même si j'avoue que cela fait un peu "réponse de normand", je dirais que... cela dépend. De plein de choses, et seul le projet peut nous permettre de juger si c'est "une bonne idée" ou non !!!
    On peux toujours trouver des cas où cela marchera. Mais d'un points de vue maintenabilité ou d'architecture logiciel, je ne voie aucun avantage. Après, il y as peut être quelques avantages intéressant qui permet de résoudre un problème précis, d'où la question

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Citation Envoyé par yan Voir le message
    Entre un pointeur null et une référence sur un objet détruit... J'ai rien contre les références, au contraire, mais pour des fonctions.
    Comme je te l'ai dit, tout dépend du contexte...

    Imagine une application qui suivrait à peu près le raisonnement suivant :
    1. lire le fichier X pour remplir une liste A
    2. lire le fichier Y pour remplir une liste B d'objet prenant en référence un objet de la liste A
    3. faire ce qu'il y a à faire
    4. vider la liste B
    5. vider la liste A
    6. reprendre au début

    La liste A pourrait parfaitement être des ressources, comme des textures, des modélisations d'objets ou va savoir quoi et liste B une série d'objets à manipuler, la boucle tournant sur un "niveau de jeu", par exemple.

    Dans un tel contexte, tu a la garantie par conception et par construction que les objets référencés par ceux qui se trouvent dans la liste B auront une durée de vie (très légèrement) plus grande que les objets qui les référencent, et donc, qu'il n'y aura jamais le moindre problème
    comment tu utilise cette classe??
    En utilisant le CRTP, sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class MaClasse : public Interface<MaClasse>{
    public:
        MaClasse(/*paramètres*/):Interface<MaClasse>(*this)/*, membres propres à MaClasse*/{}
        /* ... */
    };
    Ca a l'air bizarre, je sais, mais ca fonctionne parfaitement, et permet même le cas échéant aux fonctions membre de Interface de profiter du polymorphisme au sujet de la classe dérivée
    On peux toujours trouver des cas où cela marchera. Mais d'un points de vue maintenabilité ou d'architecture logiciel, je ne voie aucun avantage. Après, il y as peut être quelques avantages intéressant qui permet de résoudre un problème précis, d'où la question
    Comme je te l'ai dit plus tôt, tant que tu as la garantie que les objets référencés ont une durée de vie supérieure à celles des objets référent, cela ne pose pas plus de problème de maintenabilité que les pointeurs; qu'ils soient intelligents ou non. Avec l'avantage de la non nullité des références

    Donc, oui, je conçois parfaitement que tu t'interroges sur cette construction bizarre, et seule la connaissance du projet dans lequel tu l'as rencontrée pourra te confirmer qu'elle ne pose aucun problème, mais elle ne me choque pas à partir du moment où les garanties sont apportées par construction

    Tu sais, cette discussion me fait un peu penser à toutes celles que l'on a déjà eues (pas forcément toi et moi, mais sur le forum en général ) au sujet du singleton dans lesquelles on dit et répète que le meilleur moyen pour s'assurer qu'un objet ne soit construit qu'une seule fois est encore de s'assurer de n'en créer qu'une seule instance à un point bien particulier.

    La seule différence ici, c'est que le meilleur moyen de s'assurer qu'il n'y aura pas de problème est de s'assurer que, quoi qu'il arrive, les objets référencés soient détruits après les objets référençants... Mais, à partir du moment où cette garantie est présente, il n'y a aucune raison pour qu'il n'y ait le moindre problème
    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
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Citation Envoyé par yan Voir le message
    Qu'es ce qui peux garantir que la référence sera toujours valide dans le temps? A par le développeur d'origine?
    Certaines constructions t'offrent cette garantie, sinon faut faire confiance au dev.

    Citation Envoyé par yan Voir le message
    Entre un pointeur null et une référence sur un objet détruit... J'ai rien contre les références, au contraire, mais pour des fonctions.
    C'est un exemple de construction qui te garanti que la ref sera valide.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void foo(Bar & bar) {
       // pas de problèmes de ref détruite ici dans un contexte mono-thread
       // dans un contexte multi-threads il faut quand même s'assurer qu'un autre thread ne détruira pas "bar"
    }
    Citation Envoyé par yan Voir le message
    Pour le problème de possession partager, on pourrais utiliser un weak_ptr. Cela permettra au moins de savoir si l'objet existe avant de l'utiliser et être sur qu'il ne sera pas détruit pendant son utilisation.
    Si l'objet est partagé, oui, le couple std::shared_ptr / std::weak_ptr fait le boulot.

    Citation Envoyé par yan Voir le message
    Pourquoi un impact sur les performances?
    Un std::shared_ptr compte les références sur l'objet pour le garder en vie tant qu'il reste au moins 1 ref, le comptage de références c'est pas gratuit.

  8. #8
    Membre émérite
    Avatar de prgasp77
    Homme Profil pro
    Ingénieur en systèmes embarqués
    Inscrit en
    Juin 2004
    Messages
    1 306
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur en systèmes embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Juin 2004
    Messages : 1 306
    Points : 2 466
    Points
    2 466
    Par défaut
    Il y a un point que personne n'a relevé, je crois : celui de l'usage explicite. Lorsque je vois une classe obiwan ayant pour constructeur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class obiwan
    {
        obiwan(jarjarbinks& bff);
        /* ... */
    };
    Je comprends son usage : je dois fournir lors de la création d'un objet de type obiwan une référence vers un objet de type jarjarbinks dont la durée de vie égale au moins celle d'obiwan. C'est évident, même pas besoin de lire la doc !

    Ce n'est pas un avantage vis-à-vis de l'emploi de pointeurs intelligents, qui eux aussi explicitent l'usage qui sera fait de la mémoire et des garanties que doit fournir l'utilisateur de la classe. Mais ça fait de l'usage de référence une alternative au moins aussi bonne à l'usage de pointeur intelligent.
    -- Yankel Scialom

  9. #9
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    En utilisant le CRTP, sous une forme proche de
    C'est ce donner du mal pour cacher un pointeur quand même :p

    sinon faut faire confiance au dev.
    lol ^^ C'est facile quand t'es tout seule, mais en équipe, rien ne garantie qu'un des dev ne fera pas avoir.


    C'est un exemple de construction qui te garanti que la ref sera valide.
    Ma question est d'avoir un membre d'un classe qui est une référence pas sur l'utilisation de la référence en elle même.

    Un std::shared_ptr compte les références sur l'objet pour le garder en vie tant qu'il reste au moins 1 ref, le comptage de références c'est pas gratuit.
    Le comptage de ref n'interviens uniquement lors de la création/destruction d'un shraed_ptr, non?

    Je comprends son usage : je dois fournir lors de la création d'un objet de type obiwan une référence vers un objet de type jarjarbinks dont la durée de vie égale au moins celle d'obiwan. C'est évident, même pas besoin de lire la doc !
    ??
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    obiwan* CreateInstance()
    {
    jarjarbinks   b;
    return new obiwan(b);
    }
    Si obiwans garde une refencre sur jarjarbinks => on est mal barré. Et rien ne m’empêche de le faire. Sauf la doc.

    La référence est juste un pointeur contant qui ne doit pas être null . Mais c'est bien un pointeur et donc il as les même problème de durée de vie de l'instance qu'un pointeur brut. Ce que semble oublier beaucoup de développeur.

  10. #10
    Membre émérite
    Avatar de prgasp77
    Homme Profil pro
    Ingénieur en systèmes embarqués
    Inscrit en
    Juin 2004
    Messages
    1 306
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur en systèmes embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Juin 2004
    Messages : 1 306
    Points : 2 466
    Points
    2 466
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    obiwan* CreateInstance()
    {
    jarjarbinks   b;
    return new obiwan(b);
    }
    Le développeur qui écrit ça en sachant que obiwan prend une référence sur un jarjarbinks est soit mal intentionné, soit vraiment mauvais débutant. Dans les deux cas, tu ne peux rien y faire.

    Citation Envoyé par yan Voir le message
    La référence est juste un pointeur contant qui ne doit pas être null . Mais c'est bien un pointeur et donc il as les même problème de durée de vie de l'instance qu'un pointeur brut. Ce que semble oublier beaucoup de développeur.
    Techniquement, la référence est un pointeur ; conceptuellement et sémantiquement il n'en est qu'un lointain cousin. prendre une référence en argument pour un constructeur c'est CRIER SUR TOUS LES TOITS qu'on demande que la référence reste valide durant toute la durée de l'objet créé. L'utilisateur de la classe saura qu'il doit assurer cette durée de vie. S'il ne le fait pas (car techniquement, il peut ne pas le faire), c'est qu'il ne veut pas le faire. Et là, s'il veut contourner la règle, qu'elle soit technique ou pas, il y arrivera.
    -- Yankel Scialom

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Citation Envoyé par yan Voir le message
    C'est ce donner du mal pour cacher un pointeur quand même :p
    non, c'est créer une inteface qui peut être utilisée avec plusieurs classes n'ayant aucun lien entre elles du point de vue du LSP...

    Ce n'est, par exemple, pas parce que tous tes objets sont "drawable" qu'il doivent tous hériter d'un même "god object", et l'avantage par rapport aux interface java, c'est que tu peux parfaitement leur donner des données membres spécifiques à cette interface, et donc, définir un comportement bien précis pour la/les fonctions exposée(s) par cette interface
    lol ^^ C'est facile quand t'es tout seule, mais en équipe, rien ne garantie qu'un des dev ne fera pas avoir.
    Non, il faut que ce soit inscrit dans la conception, je suis d'accord. Mais si un développeur ne respecte pas cette contrainte de collection, il passe par la fenêtre comme tout développeur ne respectant pas une contraire de conception, c'est aussi simple que cela
    Ma question est d'avoir un membre d'un classe qui est une référence pas sur l'utilisation de la référence en elle même.
    Ben, de toutes façon, si tu as une variable membre -- quel que soit le type de cette variable -- c'est sans doute parce que tu voudra l'utiliser par la suite. Le fait que ce soit une référence ne pose -- a priori -- aucun problème, pour peu que tu puisse garantir par conception le fait que l'objet référencé existera tant que l'objet référant existe. Il n'y a pas à aller plus loin que cela

    Le comptage de ref n'interviens uniquement lors de la création/destruction d'un shraed_ptr, non?
    Si tu compte la copie dans le processus de création, oui, c'est à peu près cela, en effet

    ??
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    obiwan* CreateInstance()
    {
    jarjarbinks   b;
    return new obiwan(b);
    }
    Si obiwans garde une refencre sur jarjarbinks => on est mal barré. Et rien ne m’empêche de le faire. Sauf la doc.

    La référence est juste un pointeur contant qui ne doit pas être null . Mais c'est bien un pointeur et donc il as les même problème de durée de vie de l'instance qu'un pointeur brut. Ce que semble oublier beaucoup de développeur.
    A ceci près que tu n'as aucun moyen de savoir si tu invoque delete sur un pointeur nu. Alors qu'il est tout à fait possible de définir très clairement quand un objet (non pointeur) référencé par "un autre objet" sera détruit, car ce sera fait :
    • soit lorsque l'on décidera de le retirer de la collection dans laquelle il se trouve
    • soit lorsque l'on décidera de détruire la collection dans laquelle il se trouve.

    Tout ce qu'il faut donc, c'est de s'assurer que ces deux étapes arrivent après la destruction de l'objet référant, et basta
    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

  12. #12
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Citation Envoyé par yan Voir le message
    C'est ce donner du mal pour cacher un pointeur quand même :p
    Comme tu le dis une ref n'est rien d'autre qu'un pointeur qui pointe forcément sur quelque chose (valide ou non).
    Tu peux utiliser un pointeur nu si tu veux, mais on a la garantie ici que le pointeur pointera sur quelque chose (et quelque chose de valide) : autant l'indiquer en utilisant une ref, ça rend le code plus facile à lire / comprendre.

    Citation Envoyé par yan Voir le message
    Ma question est d'avoir un membre d'un classe qui est une référence pas sur l'utilisation de la référence en elle même.
    C'est exactement le même problème, quand on utilise des refs / pointeurs il faut s'assurer que ça pointe sur quelque chose de valide.
    Si tu peux fournir cette garantie une ref c'est cool, sinon il faudra copier l'objet (ok, c'est pas aussi simple, mais presque).

    Citation Envoyé par yan Voir le message
    Le comptage de ref n'interviens uniquement lors de la création/destruction d'un shraed_ptr, non?
    + Toutes les copies du pointeur (à chaque copie il faut mettre à jour le nombre de refs sur l'objet). + un shared_ptr est 2 fois plus grand qu'un pointeur (en interne il contient 2 pointeurs, 1 vers l'objet référencé, l'autre vers une structure pour le comptage de références) -> ça à un effet sur le cache : ça prend plus de place, donc potentiellement plus de caches misses.

    Citation Envoyé par yan Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    obiwan* CreateInstance()
    {
    jarjarbinks   b;
    return new obiwan(b);
    }
    Si obiwans garde une refencre sur jarjarbinks => on est mal barré. Et rien ne m’empêche de le faire. Sauf la doc.
    La doc est directement dispo via l'auto-complétion : une fonction (ou un ctor) qui prend une ref en paramètre -> l'objet doit être valide assez longtemps.
    Et généralement un ctor qui prend une ref non constante c'est que l'objet va garder la ref. Pour les ref constantes, oui on peut avoir à regarder la doc pour savoir si l'objet garde la ref ou l'utilise simplement pendant le ctor.

    Mais un std::shared_ptr / std::weak_ptr ne change rien : dans tous les cas il faut regarder la doc pour savoir quelle doit être la durée de vie exacte de l'objet référencée.
    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
    struct Foo {
       int& i;
     
       Foo(int& i): i(i) { }
       void f() const {
          std::cout << i << std::endl; // la ref doit être valide sinon crash
       }
    };
     
    struct Bar {
       std::shared_ptr<int> i;
       Bar(std::shared_ptr<int> i): i(i) { }
       void f() const {
          std::cout << *i << std::endl; // la ref est valide, mais on étend la durée de vie de l'objet référencée
          // ce qui n'est pas forcément voulu
       }
    };
     
    struct FooBar {
       std::weak_ptr<int> i;
       Bar(std::weak_ptr<int> i): i(i) { }
       void f() const {
          auto si = i.lock(); // on récupère un shared_ptr et étend la durée de vie le temps de la fonction
          // ce qui n'est pas forcément voulu
          if(!si) {
             // l'objet à été détruit, qu'est ce qu'on fait ?
          }
          else {
             std::cout << *si << std::endl; // la ref est valide
          }
       }
    };
    Au final c'est une gestion d'erreurs à la compilation vs exécution. Si l'objet référencé DOIT être valide alors une ref (ou pointeur nu) fait l'affaire, on se démerde pour garantir qu'il soit valide quand on en a besoin.

  13. #13
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Citation Envoyé par prgasp77 Voir le message
    prendre une référence en argument pour un constructeur c'est CRIER SUR TOUS LES TOITS qu'on demande que la référence reste valide durant toute la durée de l'objet créé.
    Personnellement, c'est la première fois que mon dit cela. Mais bon on m'as appris à faire du C avec le C++ à l'école , mais je me rattrape. Depuis que je travaille, j'ai vue de tout et même beaucoup de personne qui ne connaissent pas les const ou les références . Tu peux le spécifier dans les règles de codage, mais il ne me semble pas que ce soit une norme.

    Quand je vois un constructeur
    • avec une référence const => la classe va utiliser l'objet dans le constructeur.
    • référence non const => le développeur a oublié le const ou il veux modifier l’objet dans le constructeur.

    Mais à aucun moment, je m'attend à ce qu'il garde un pointeur/référence sur cette objet. Dans les bibliothèque que j'ai pu utiliser, je n'ai jamais vue de classe avec un membre référence.


    non, c'est créer une interface qui peut être utilisée avec plusieurs classes n'ayant aucun lien entre elles du point de vue du LSP...
    Je parlais d'utiliser une référence plutôt qu'un pointeur. Tu déréférence this pour pouvoir le mettre dans une référence.

    A ceci près que tu n'as aucun moyen de savoir si tu invoque delete sur un pointeur nu. Alors qu'il est tout à fait possible de définir très clairement quand un objet (non pointeur) référencé par "un autre objet" sera détruit, car ce sera fait :
    • soit lorsque l'on décidera de le retirer de la collection dans laquelle il se trouve
    • soit lorsque l'on décidera de détruire la collection dans laquelle il se trouve
    Je n'ai pas compris ce que tu voulais dire. Car si l'instance est retiré de la collection la reference sera invalide. C'est même chose que l'utilisation d'un pointeur nue. Dans ce cas il faut mieux utiliser les smart pointeur (pour moi).

    Pour moi, l'utilisation d'une référence doit me garantir que l'objet est valide. Si il peut y avoir un problème sur sa validité, alors pointeur ou pointeur intelligent.

    Et fournir un classe avec la possibilité que le membre référence deviens invalide si un développeur change quelques lignes de code est pour moi un problème de conception et de maintenabilité. A un instant t, effectivement çà fonctionnera. Mais plus tard après une évolution/correction/.. ?

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Citation Envoyé par yan Voir le message
    Personnellement, c'est la première fois que mon dit cela. Mais bon on m'as appris à faire du C avec le C++ à l'école , mais je me rattrape. Depuis que je travaille, j'ai vue de tout et même beaucoup de personne qui ne connaissent pas les const ou les références . Tu peux le spécifier dans les règles de codage, mais il ne me semble pas que ce soit une norme.

    Quand je vois un constructeur
    • avec une référence const => la classe va utiliser l'objet dans le constructeur.
    • référence non const => le développeur a oublié le const ou il veux modifier l’objet dans le constructeur.

    Mais à aucun moment, je m'attend à ce qu'il garde un pointeur/référence sur cette objet. Dans les bibliothèque que j'ai pu utiliser, je n'ai jamais vue de classe avec un membre référence.
    Encore une fois, je suis d'accord avec toi sur le fond. Mais, si la doc ou la conception spécifie que l'objet doit rester valide, celui qui ne respecte pas cette spécification est bon pour passer par la fenêtre et ne peut s'en prendre qu'à lui-même
    Je parlais d'utiliser une référence plutôt qu'un pointeur. Tu déréférence this pour pouvoir le mettre dans une référence.
    Tout à fait d'accord avec toi sur le fond, encore une fois.

    Sauf que, c'est plus fort que moi, lorsque je croise un pointeur, la première chose que je vais faire c'est m'assurer de sa validité, soit par assertion (si la non validité est une erreur de la part du programmeur) sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    void MaClasse::foo(){
       assert(m_ptr!=null && "Null pointer deferenced");
    }
    soit sous la forme classique d'un test s'il est possible que le pointeur soit null sans représenter une erreur de programmation sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void MaClasse::foo(){
        if(m_ptr !=null /* && m_ptr->bar() */){
            /*   ... */
       }
    }
    Or, si je remplace le pointeur que j'ai appelé m_ptr dans ces deux exemples par une référence, je n'ai pas à m'inquiéter de la validité de celle-ci (je n'ai d'ailleurs aucun moyen de le faire) : je peux partir du principe que la référence est valide et que, si ce n'est pas le cas c'est que quelqu'un n'a pas suivi les spécifications.

    Je n'ai pas compris ce que tu voulais dire. Car si l'instance est retiré de la collection la reference sera invalide. C'est même chose que l'utilisation d'un pointeur nue. Dans ce cas il faut mieux utiliser les smart pointeur (pour moi).
    C'est bien pour cela que tu dois t'arranger pour garantir que l'objet référencé aura une durée de vie plus grande que l'objet référençant. Mais, si tu apporte cette garantie par la conception même du programme, par l'ordre dans lequel tu effectue les différentes actions, je n'ai absolument aucun problème avec l'idée de le faire
    Pour moi, l'utilisation d'une référence doit me garantir que l'objet est valide. Si il peut y avoir un problème sur sa validité, alors pointeur ou pointeur intelligent.
    C'est là, à mon sens, le point où nos avis divergent : pour moi, une référence ne doit rien garantir de particulier.

    Elle se contente d'apporter une garantie simple :lorsque la référence est créée, l'objet existe. Ce n'est pas son role de garantir que l'objet référencé continue à exister. En vertu du SRP, le fait d'apporter cette garantie échoit à "autre chose"
    Et fournir un classe avec la possibilité que le membre référence deviens invalide si un développeur change quelques lignes de code est pour moi un problème de conception et de maintenabilité. A un instant t, effectivement çà fonctionnera. Mais plus tard après une évolution/correction/.. ?
    C'est bien pour cela que c'est une garantie qui doit faire partie des spécifications, et que tout développeur se doit de faire en sorte d'apporter

    Encore une fois, je comprend très bien ton malaise face à cette situation. Mais dis toi que, si la garantie est bel et bien présente au départ et que les principes SOLID (essentiellement SRP, dans ce cas) sont bien respectés, ce n'est pas en changeant "quelques lignes de code" que tu pourras "casser" cette garantie
    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

  15. #15
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Citation Envoyé par yan Voir le message
    Dans les bibliothèque que j'ai pu utiliser, je n'ai jamais vue de classe avec un membre référence.
    Un exemple plus ou moins courant : les lambdas avec capture par référence
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int i = 42;
    auto foo = [&i]() { };
     
    // equivalent à 
    struct foo_{
       foo_(int& i): i_(i) { }
       void operator()() { }
    private:
       int & i_;
    };
    foo_ foo(i);
    Citation Envoyé par yan Voir le message
    Pour moi, l'utilisation d'une référence doit me garantir que l'objet est valide. Si il peut y avoir un problème sur sa validité, alors pointeur ou pointeur intelligent.
    C'est l'inverse, la référence ne garantie rien (ou presque), mais tu dois fournir des garanties pour l'utiliser.

  16. #16
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Citation Envoyé par Iradrille Voir le message
    Un exemple plus ou moins courant : les lambdas avec capture par référence.
    ? On n'utilise pas une fonction lambda comme une classe. Ce n'est pas la même chose. Ou alors tu parle de foncteur.

    Lorsque tu utilise la capture par référence c'est généralement pour une utilisation ponctuelle ou interne à la classe. Qu'un lambda avec capture par référence se retrouve exécuté hors de ce contexte et à ma connaissance un abus de pratique du lambda. Et tu te retrouve avec le problème de validité des références.

  17. #17
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 113
    Points : 32 958
    Points
    32 958
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par yan Voir le message
    Et rien ne m’empêche de le faire. Sauf la doc.
    Rien ne m'empêche non plus de faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int i;
    int& j = i;
    int*k = &i;
    delete k;
    Bref, lire la doc et faire confiance aux dev c'est quand même un minimum.. sinon on arrête de coder dans l'immédiat.
    Une classe qui demande une référence, tu lis la doc, tu vois quand/comment elle l'utilise, et tu te débrouilles pour bien l'utiliser.
    Ou t'utilises une autre classe.


    Lorsque tu utilise la capture par référence c'est généralement pour une utilisation ponctuelle ou interne à la classe.
    En quoi une référence dans une fonction ou lambda est plus ponctuelle que dans une classe ?
    La notion de portée et durée de vie d'une variable n'est pas étranger à un objet créé via une classe.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  18. #18
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Citation Envoyé par yan Voir le message
    ? On n'utilise pas une fonction lambda comme une classe. Ce n'est pas la même chose. Ou alors tu parle de foncteur.

    Lorsque tu utilise la capture par référence c'est généralement pour une utilisation ponctuelle ou interne à la classe. Qu'un lambda avec capture par référence se retrouve exécuté hors de ce contexte et à ma connaissance un abus de pratique du lambda. Et tu te retrouve avec le problème de validité des références.
    De lamdba à foncteur il n'y à qu'un pas. Mais encore une fois ce n'est pas un "problème", c'est une simple utilisation de référence (et il faut fournir les garanties qui vont bien).

    T'as un background Java / C# ? On dirait que tu cherches une équivalence référence Java / C# <=> référence C++.
    Le plus proche que tu trouveras c'est références Java / C# <=> std::shared_ptr.

    Les références C++ n'ont à ma connaissance pas d'équivalent en Java, et vu que c'est quasiment des pointeurs, l'équivalent le plus proche en C# c'est les pointeurs (unsafe).

  19. #19
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Bref, lire la doc et faire confiance aux dev c'est quand même un minimum..
    Disons qu'à un instant T, tout le monde est en phase et que personne ne fait n'importe quoi .Plus tard, lorsque le code doit être modifier, mise à jour, repris, ... je trouve que dans un cas générale d'avoir une référence en membre d'un classe est source d'erreurs. Je ne parle pas de foncteur ou de classe que l'on utilise ponctuellement comme std::lock_guard.

    En quoi une référence dans une fonction ou lambda est plus ponctuelle que dans une classe ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    void foo(bar &b) {...};
    /*...*/
    bar toto;
    foo(toto);
     
     
    std::for_each( t.begin(), t.end(), [&](int t){...});
    Quand on appel la fonction foo ou que l'on applique le lambda sur la collection, la référence est forcement valide. Alors que pour une classe, la gestion de vie peut être plus complexe et source de bug.

    De lamdba à foncteur il n'y à qu'un pas.
    Je n'ai pas dit le contraire

    On dirait que tu cherches une équivalence référence
    Finalement, c'est peut être cela qui m'a posé problème et d'où ma question d'origine. Le code (du client) utilise des références en membres de classe là où cela aurais dû être des shared_ptr. Et justement un des problèmes que j'ai rencontré était lié à cela et au “static initialization order fiasco”. Des références sur des données static étaient utilisées alors que les données n'avaient pas encore été créées/initialisées.
    Et c'est depuis que j'y vois plus de problèmes que de solution.

    En tout cas merci pour vos réponses.

    Yan

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Citation Envoyé par yan Voir le message
    Disons qu'à un instant T, tout le monde est en phase et que personne ne fait n'importe quoi .Plus tard, lorsque le code doit être modifier, mise à jour, repris, ... je trouve que dans un cas générale d'avoir une référence en membre d'un classe est source d'erreurs.
    Sans doute (même très certainement ), si on demande à un stagière arrivé hier d'aller chipoter à ce qui garanti l'existence de l'objet référencé....

    Mais, la faute à qui si on ne lui dit pas que la première chose à faire est de respecter les spécifications qu'il pourra trouver à tel ou tel endroit

    D'autant plus que, ce genre de specs, ca doit être écrit en immense, en fluo, seul sur un mur
    Finalement, c'est peut être cela qui m'a posé problème et d'où ma question d'origine. Le code (du client) utilise des références en membres de classe là où cela aurais dû être des shared_ptr. Et justement un des problèmes que j'ai rencontré était lié à cela et au “static initialization order fiasco”. Des références sur des données static étaient utilisées alors que les données n'avaient pas encore été créées/initialisées.
    Et c'est depuis que j'y vois plus de problèmes que de solution.
    Mais le problème venait du fiasco dans l'ordre des initialisations, pas du principe qui consiste à utiliser une référence comme donnée membre...

    Cela fait pourtant longtemps que l'on répète que les variables globales sont mauvaises et dangereuses et qu'un singleton (ou une variable statique, vu que c'est la base du DP) n'est qu'une variable globale "un peu maquillé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.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Initialiser une référence membre d'une classe
    Par NicolasJolet dans le forum C++
    Réponses: 2
    Dernier message: 18/03/2006, 13h14
  2. [VB.NET]Reflexion lister les membres d'une classe ...presque
    Par lucie.houel dans le forum ASP.NET
    Réponses: 19
    Dernier message: 20/09/2005, 14h49
  3. Réponses: 3
    Dernier message: 24/04/2005, 15h19
  4. pointeur membre static de classe
    Par Ca$ul dans le forum C++
    Réponses: 3
    Dernier message: 26/08/2004, 14h02
  5. Thread avec une fonction membre d'une classe
    Par SteelBox dans le forum Windows
    Réponses: 6
    Dernier message: 01/03/2004, 02h15

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