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 :

Retour par référence d'attribut de classe


Sujet :

C++

  1. #1
    Modérateur
    Avatar de grunk
    Homme Profil pro
    Lead dév - Architecte
    Inscrit en
    Août 2003
    Messages
    6 693
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Lead dév - Architecte
    Secteur : Industrie

    Informations forums :
    Inscription : Août 2003
    Messages : 6 693
    Par défaut Retour par référence d'attribut de classe
    Bonjour à tous,

    Je m'interroge sur la façon de faire des getter sur des membres de classe.
    En imaginant la classe suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class MaClasse
    {
        public:
            MonObjet GetObjet();
            MonObjet& GetObjet2();
     
        private:
            MonObjet mObjet;
    }
    Mon background de dev java me fait plutôt pencher vers un retour par référence sur un Getter.
    Cependant j'ai l'impression qu'en C++ c'est une mauvaise idée car si je fait :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    MonObjet& resultat = o.GetObjet2();
    //Ici destruction de MaClasse pour X raisons
    resultat.methode(); // Crash ?
    A aucun moment une référence sur un objet va m’empêcher de détruire l'objet initial et par conséquent me mettre quelque peu dans la mouise me trompe je ?

    Du coup un retour par copie me semble plus sur , mais cela veux également dire que si l'objet retourné fait 10M en ram , je vais bouffer 10M pour potentiellement pas grand chose le temps que le scope de la copie se termine, non ?

    Bref je suis un peu perdu sur la solution à adopter.

    Toujours en se basant sur l'exemple de classe précédent si je fait un setter de ce type :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void Classe::SetObjet(MonObjet obj)
    {
        mObjet = obj;
    }
    Que devient la mémoire précédement allouée par mObjet ? Est ce qu'elle sera libérée une fois la classe complète détruite ou simplement lors de la nouvelle copie ?

    Merci
    Pry Framework php5 | N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  2. #2
    Membre Expert
    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 : 38
    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
    Par défaut
    Bonjour,

    si tu as besoin qu'un élément extérieur à ta classe accède et utilise un élément qui la compose, tu te retrouves dans un cas où plusieurs intervenants doivent se partager une ressource. J'ai moi aussi appris la POO dans les années 2000, où il était courant d'utiliser les accesseurs de manière systématique. Depuis quelques années, j'ai appris à m'en passer dans 19 cas sur 20 ; et je m'en porte bien mieux.

    Si ce besoin est confirmé, il te faut mettre en palce une stratégie de partage de ressource. Elle peut être très simple (imposer que l'objet o doit exister quand on utiliser une référence récupérée via son accesseur) ou plus échafaudée ; je pense notamment au couple std::shared_ptr et std::weak_ptr. Mais sincèrement, est-ce que cela a du sens de conserver une référence vers un élément d'un objet quand l'objet a été détruit ?

    EDIT :
    Aparté : C++ a une gestion des variables et méthodes const qui apporte beaucoup :; si tu dois écrire un getter :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class Bobafett
    {
        const Object& getObject() const;
        Object& getObject();
    }
    La première version retourne une référence vers un objet constant, il est donc possible d'appeler la méthode getObject() d'un Bobafett constant. La seconde (optionnelle), permet de modifier l'objet retourné, elle ne peut être appelée sur un Bobafett non constant.


    Un point que je n'ai pas abordé et c'est une erreur. Tu veux peut-être partager une ressource qui n'appartiendrait pas à la classe.
    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
    class Obiwan
    {
        Object& _resource;
    public:
        Obiwan() : delete;
        Obiwan(Object& resource) : _resource(resource) {}
        /* Copy, assignment, move ... */
        const Object& resource() const { return _resource; }
        Object& getObject() { return _resource; }
    }
     
     
    int main(void)
    {
        Object theResource;
        Obiwan kenobi(theResource);
       /* ... */
        const Object& kenobisobject = kenobi.resource();
    Les contraintes sont les même que dans l'exemple que tu as cité, mais à la simple vue de la déclaration de la classe Obiwan, un développeur saura les contraintes imposées à l'utilisabilité de la référence accédée.

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

    De manière générale, les accesseurs ( == getters )ne doivent faire qu'une seule et unique chose : permettre d'obtenir une valeur particulière (sous entendu : en aucun cas permettre de modifier cette valeur).

    Il y a bien quelques cas particuliers dans lesquels il est "opportun" de permettre de modifier la valeur obtenue (ex : l'opérateur [] de la classe std::vector), mais, dans l'ensemble, ils sont vraiment particulièrement rares

    C'est la raison pour laquelle nous insistons toujours énormément sur le fait que les accesseurs soient fournis sous la forme de fonctions constantes uniquement. Cela permettra au compilateur de t'engueuler s'il se rend compte que le "contrat" passé par cette fonction ("je m'engage à ne pas modifier l'objet courant") devait ne pas être respecté

    Du coup, tu peux soit renvoyer la donnée en question par valeur, soit le faire sous la forme d'une référence constante (si tant est que ce soit bel et bien un accesseur sur une donnée membre de la classe et non sur une donnée qui doit être évaluée à chaque passage dans la fonction).

    A noter que prgasp77 a tout à fait raison : si tu t'amuses à fournir un "plein et entier" sous la forme d'un accesseur et d'un mutateur à toutes les données membres de ta classe, tu perd littéralement tout l'avantage de l'encapsulation.

    Du coup, il ne vaut la peine de fournir un accesseur sur une donnée que si cela correspond à un service que l'on est effectivement en droit d'attendre de la part de la classe développée :
    on s'attend à ce qu'une classe personne puisse répondre aux question Quel est ton nom? et Quel est ton prénom, à ce qu'une classe Date puisse répondre aux question Quel jour? Quel mois ou Quelle année ? etc, ce qui justifie clairement la présence de fonctions permettant de répondre à ces questions.

    Par contre, la loi de Déméter nous dit que l'on ne devrait même pas savoir que la notion de RéservoirDeCarburant existe pour utiliser la notion de Voiture. Il ne sert absolument à rien de fournir une fonciton getReservoire

    Et je m'arrêterai là et ne parlerai des mutateurs que si tu en fais explicitement la demande
    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

  4. #4
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 766
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 766
    Par défaut
    Citation Envoyé par grunk Voir le message
    Toujours en se basant sur l'exemple de classe précédent si je fait un setter de ce type :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void Classe::SetObjet(MonObjet obj)
    {
        mObjet = obj;
    }
    Que devient la mémoire précédement allouée par mObjet ? Est ce qu'elle sera libérée une fois la classe complète détruite ou simplement lors de la nouvelle copie ?
    Il faut faire gaffe , mais avec le C++11/ C++14 et les déplacements (move)/ Rvalue reference (&&) certaines optimisations du compilateur ont dues être supprimées.

    Mais il y avait avant par exemple, des optimisations du compilateur qui s'appellent RVO (return value optimization) et NRVO (named return value optimization).

    Dans ton exemple, il y a 2 recopies: 1 pour le passage de paramètre (copie 1) et 1 à l'intérieur de ta méthode (copie 2)
    Mais un cas trivial comme dans ton exemple, le compilateur devrait détecter que ta méthode est simple (1 seule affectation) et devrait ne pas faire qu'1 seule copie (par contre peut-être que c'est avec un passage constant par valeur )

    Mais ton exemple est mauvais, parce que tu fais une copie dans un objet temporaire qui est détruit après l'appel.
    L'objet que tu passeras en paramètre ne sera pas modifié

  5. #5
    Modérateur
    Avatar de grunk
    Homme Profil pro
    Lead dév - Architecte
    Inscrit en
    Août 2003
    Messages
    6 693
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Lead dév - Architecte
    Secteur : Industrie

    Informations forums :
    Inscription : Août 2003
    Messages : 6 693
    Par défaut
    Merci à tous pour vos réponses , c'est plus clair
    Pry Framework php5 | N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  6. #6
    Rédacteur/Modérateur


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

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 147
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par grunk Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    MonObjet& resultat = o.GetObjet2();
    //Ici destruction de MaClasse pour X raisons
    resultat.methode(); // Crash ?
    o est lui-meme une reference vu la syntaxe, si effectivement ta reference est detruite entre temps, t'as un serieux probleme.

    Citation Envoyé par grunk Voir le message
    A aucun moment une référence sur un objet va m’empêcher de détruire l'objet initial et par conséquent me mettre quelque peu dans la mouise me trompe je ?
    En C++ a aucun moment on t'empeche de te tirer une balle dans la tete, pour autant on evitera.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void f(const T& t) { delete const_cast<T*>(&t); }
    Est possible, moyennant d'eventuelles erreurs de compile, pour autant tu le verras surement jamais en "vrai".
    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.

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

Discussions similaires

  1. retour par référence et new
    Par ellosap dans le forum Débuter
    Réponses: 10
    Dernier message: 28/05/2010, 18h47
  2. Retour par référence sur const
    Par Cheps dans le forum C++
    Réponses: 3
    Dernier message: 14/12/2008, 22h36
  3. Retour par référence d'un pointeur
    Par FunkyTech dans le forum C++
    Réponses: 16
    Dernier message: 22/07/2008, 13h56
  4. retour par référence de l'opérateur ++
    Par BigNic dans le forum C++
    Réponses: 4
    Dernier message: 02/08/2006, 18h35

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