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 d'objet par référence...


Sujet :

C++

  1. #1
    sas
    sas est déconnecté
    Membre éprouvé

    Profil pro
    Inscrit en
    Novembre 2003
    Messages
    54
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2003
    Messages : 54
    Points : 1 257
    Points
    1 257
    Par défaut retour d'objet par référence...
    bonjour à tous,

    voilà après avoir arpenté la faq et consulté les quelques post qui traitaient le sujet, j'ai toujours quelques doutes dans ce dommaine. Donc je m'en remets à vous pour éclaircir tout ça.

    Pour ce qui est de l'utilisation des références en paramètes de fonctions, c'est tout simple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    MaClasse::use_param_ref_const(const UneAutreClasse& a) // lecture seule
    {
        // code...
    }
     
    MaClasse::use_param_ref(UneAutreClasse& a) // lecture et écriture
    {
        // code...
    }
    mais pour le retour, y-a-t'il une différence entre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    const UneAutreClasse&
    MaClasse::return_param_ref_const(void)
    {
        // code...
    }
    // et
    UneAutreClasse&
    MaClasse::return_param_ref(void) 
    {
        // code...
    }
    sachant que l'on peut écrire (je crois) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
        UneAutreClasse UneClasse = objMaClasse.return_param_ref_const();
        UneAutreClasse UneClasse = objMaClasse.return_param_ref();
    et pour ce qui est de l'objet retourné sachant qu'un :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    (const) UneAutreClasse&
    MaClasse::return_param_ref(void) 
    {
        UneAutreClasse tmp;
        // code...
        return tmp;
    }
    ne fera que planté le prog puisque qu'un objet local sera détruit en fin de fonction (contraitement aux types prédéfinis, ex. : int, long, unsigned...., soit dit en passant je vois pas pourquoi eux aussi ne seraient pas détruits, peut être que je mélange tout...)

    pour pallier à tout ce mic-mac, j'ai recensé plusieurs solutions (mais y-en-t'il d'autres ??) :

    - utilisé une reférence sur un objet static :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    (const) UneAutreClasse&
    MaClasse::return_param_ref(void) 
    {
        static UneAutreClasse tmp;
        // code...
        return tmp;
    }
    le hic, c'est que je ne sais pas si mon static existera pour toutes mes instances de la classe MaClasse, ou bien si chaque instance de MaClasse aura son propre static. De plus sa durée de vie serait-elle équivalente à celle de sa classe qui le contient ou à tout le programme ??

    - ensuite, utilisé un objet dynamic
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    (const) UneAutreClasse&
    MaClasse::return_param_ref(void) 
    {
        UneAutreClasse tmp = new UneAutreClasse ;
        // code...
        return *tmp;
    }
    bon, problème évident c'est que ce sera à la fonction appelante de se charger du delete, pas pratique du tout ...

    - et enfin, placé la référence en param
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    void
    MaClasse::modify_param_ref(UneAutreClasse& a) 
    {
        // code qui modifie a...
    }
    à part que ça n'a plus trop une tête d'accesseur , ça le mérite de fonctionner...

    voilà exposer mes doutes, j'aimerais connaître vos opinions sur le sujet et savoir quelles solutions vous utilisez...

  2. #2
    Membre actif
    Profil pro
    Inscrit en
    Août 2003
    Messages
    247
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2003
    Messages : 247
    Points : 276
    Points
    276
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    struct T
    { void g(); }
    // Note: en C++, on écrit g() et non g(void).
     
    T& f1();
    const T& f2();
     
     
    f1().g();    // OK.
    f2().g();    // Erreur: T::g() n'est pas const.
     
    T& var1 = f1();    // OK.
    T& var2 = f2();    // Erreur: On ne peut pas convertir const T& vers T&.
    Voilà pour la première question.



    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    template<class T> T& f()
    {
        T tmp;
        return tmp;
    }
    Cette fonction échoura quelque soit T (classe et type natif compris).



    La méthode avec static ne me parait pas convenir au besoin.
    La méthode avec new n'est pas terrible non plus pour les raisons que tu cites.
    La dernière méthode est sans doute la meilleure.

    Il y a aussi cette méthode là:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    UneAutreClasse
    MaClasse::f() 
    { 
        UneAutreClasse tmp; 
        // code... 
        return tmp; 
    }


    Mais dis nous ce que tu veux faire, on pourras mieux t'aider.

  3. #3
    Rédacteur
    Avatar de bigboomshakala
    Homme Profil pro
    Consultant Web .NET
    Inscrit en
    Avril 2004
    Messages
    2 077
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Consultant Web .NET
    Secteur : Finance

    Informations forums :
    Inscription : Avril 2004
    Messages : 2 077
    Points : 2 757
    Points
    2 757
    Par défaut Re: retour d'objet par référence...
    Citation Envoyé par sas
    y-a-t'il une différence entre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    const UneAutreClasse&
    MaClasse::return_param_ref_const(void)
    {
        // code...
    }
    // et
    UneAutreClasse&
    MaClasse::return_param_ref(void) 
    {
        // code...
    }
    oui il y a une différence.
    dans le 1er cas tu renvoie une référence sur une copie et tu ne peux pas utiliser la fonction comme une lvalue (car constance).
    dans le 2è cas tu renvoie une référence sur la variable du return. là tu peux utiliser la fonction comme une lvalue, mais tu dois t'assurer que la variable du return n'est pas locale à la fonction.

    et pour ce qui est de l'objet retourné sachant qu'un :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    (const) UneAutreClasse&
    MaClasse::return_param_ref(void) 
    {
        UneAutreClasse tmp;
        // code...
        return tmp;
    }
    ne fera que planté le prog puisque qu'un objet local sera détruit en fin de fonction (contraitement aux types prédéfinis, ex. : int, long, unsigned...., soit dit en passant je vois pas pourquoi eux aussi ne seraient pas détruits, peut être que je mélange tout...)
    oui tu mélanges tout. sans le &, il s'agit d'un retour par valeur. c'est-à-dire que la variable locale est copiée et que la copie est retournée, et la variable locale est supprimée à la fin de la fonction. dans le cas d'un retour par référence, il s'agit d'une référence sur la variable du return. ça ne pose pas de problème si c'est une donnée membre puisqu'elle existera encore, par contre si c'est une variable locale, alors la référence renvoie sur rien puisque la variable est détruite.

  4. #4
    Membre à l'essai
    Profil pro
    Inscrit en
    Février 2005
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 17
    Points : 20
    Points
    20
    Par défaut
    Il me semble qu'il existe une solution glauque:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
     
    const UneAutreClasse&
    MaClasse::return_param_ref(void)
    {
        // ... code , esperons que ce n'est pas sur le futur tmp
        // sinon tu peut faire une construction par copie a la fin
        return UneAutreClasse tmp;
    }
    Le compilateur te mettra un zoli warning ( hoooooo) du genre : je prolonge la vie de cette variable: tmp.

    Cette solution n'est bien sur pas valable.

    Pourquoi ne pas utilisé les pointeurs ? En général on evite d'avoir un protype de fonction de ce genre.

  5. #5
    Membre actif
    Profil pro
    Inscrit en
    Août 2003
    Messages
    247
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2003
    Messages : 247
    Points : 276
    Points
    276
    Par défaut
    Citation Envoyé par Cunixsvp
    Pourquoi ne pas utilisé les pointeurs ? En général on evite d'avoir un protype de fonction de ce genre.
    Ah bon ? J'aimerais bien savoir pourquoi. En C++, on évite plutôt d'utiliser des pointeurs.

  6. #6
    Membre à l'essai
    Profil pro
    Inscrit en
    Février 2005
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 17
    Points : 20
    Points
    20
    Par défaut
    On évite d'avoir des pointeurs ?

    Je pense que les fonctions qui alloue une instance d'une classe doivent retourner un pointeur.

    Pour justement le cas cité au dessus.

    edit: En plus si un jours tu dois faire un compilateur tu sera bien comptent d'avoir les pointeurs

  7. #7
    Membre à l'essai
    Profil pro
    Inscrit en
    Février 2005
    Messages
    17
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 17
    Points : 20
    Points
    20
    Par défaut
    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
     
    class A
    {
    A(int i);
    ...
    };
     
     
    // cas B contiens 0 ou 1 seul A
    class B
    {
    ...
    private
    A* leA;
    };
     
    // cas C contiens 0 ou 1 seul A
     
    class C
    {
    ...
    private
    A& leA;
     
    };
    Qui ne compile pas ? Qui est correcte ? Une référence ne peut être nul, donc soit on se complique la vie en utilisant du design patern singleton pour faire une class qui hérite de A et qui explicit le fait qu'il n'y as pas de A, soit on utilise un pointeur.

  8. #8
    Membre actif
    Profil pro
    Inscrit en
    Août 2003
    Messages
    247
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2003
    Messages : 247
    Points : 276
    Points
    276
    Par défaut
    Citation Envoyé par Cunixsvp
    Je pense que les fonctions qui alloue une instance d'une classe doivent retourner un pointeur.
    Dans ce cas là, d'accord.


    edit: En plus si un jours tu dois faire un compilateur tu sera bien comptent d'avoir les pointeurs
    C'est vrai que dire que l'on évite d'utiliser les pointeurs est un peu manichéen. Disons que quand on pourrait très bien utiliser autre chose de moins contraignant (les références par exemple), on utilise quelque chose de moins contraignant. C'est le cas ici.

  9. #9
    sas
    sas est déconnecté
    Membre éprouvé

    Profil pro
    Inscrit en
    Novembre 2003
    Messages
    54
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2003
    Messages : 54
    Points : 1 257
    Points
    1 257
    Par défaut
    Citation Envoyé par Selenite

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    UneAutreClasse
    MaClasse::f()
    {
        UneAutreClasse tmp;
        // code...
        return tmp;
    }
    ha ben j'avais oublier celle là, ce qui correspont à : (mais pour les objets)
    Citation Envoyé par bigboomshakala
    oui tu mélanges tout. sans le &, il s'agit d'un retour par valeur. c'est-à-dire que la variable locale est copiée et que la copie est retournée, et la variable locale est supprimée à la fin de la fonction. dans le cas d'un retour par référence, il s'agit d'une référence sur la variable du return. ça ne pose pas de problème si c'est une donnée membre puisqu'elle existera encore, par contre si c'est une variable locale, alors la référence renvoie sur rien puisque la variable est détruite.
    Citation Envoyé par Selenite
    Mais dis nous ce que tu veux faire, on pourras mieux t'aider.
    j'ai commencé à développer un petit soft de gestion... J'utilise des classes comme CProduit, CClient, CCommande, etc... et des classes de liste comme CProduitList, CClientList, etc....(un membre privé défini sur std::vector<CProduit>, pour la classe CProduitList, pareil pour les autres). Enfin une tierce classe nommé CDataBase devra me générer ces listes suivant le type de l'objet contenu dans le vector.
    ex.:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    (const) CProduitList& 
    CDataBase::getProduitList(const CProduit& param)
    {
        // code...
        return ... // retour d'un (const) CProduitList &
    }
    Mais bon, toute ma quiétude était de savoir comment retourner cette classe d'objet liste. D'où mon post qui dans sa synthèse pourrait me donner les acquis nécessaires à toutes mes programmations avenir qui aborderaient ce thème.

    et encore merci de toutes vos précisions.

  10. #10
    Membre actif
    Profil pro
    Inscrit en
    Août 2003
    Messages
    247
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2003
    Messages : 247
    Points : 276
    Points
    276
    Par défaut
    Pour construire une liste. Le plus simple en serait-il pas d'utiliser le constructeur de le liste ?

  11. #11
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 279
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 279
    Points : 11 015
    Points
    11 015
    Par défaut
    D'abord
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    T const& f();
    T  g();
    ...
    T      & t1 = f(); // pas bon
    T const& t2 = f(); // ok
    T      & t3 = g(); // ok
    T const& t4 = g(); // ok
    Si tu modifies par un autre chemin ce qui est renvoyé par f ou g, t2, t3 et t4 seront modifiés.
    Si tu modifies t3, ce qui est renvoyé par g l'est également.


    Sinon en général, en C++ on va renvoyer une copie quand on veut renvoyer une valeur tout fraiche. Certains compilos arrivent à optimiser le retour (si certaines conditions sont respectées) et faire en sorte que l'on n'ait plus (ou moins) de copie.


    Renvoyer une référence sur un pointeur fraichement alloué est une mauvaise idée. Quand je récupère une référence, je ne me pose pas la question de la libération.

    Quand on récupère un pointeur, il faut regarder la doc pour savoir comment doit vivre l'objet.

    Quand on récupère un auto_ptr<>, on est sûr que l'objet devra être détruit (ça tombe bien, std::auto_ptr s'en chargera si on ne lui dit rien). Un auto_ptr<> en sortie signifie "je suis source d'une nouvelle donnée", en entrée: "je suis puit d'une donnée".
    Plein d'info interressantes (haut niveau) sur CUJ, chercher MOJO ; on peut aussi partir du site d'Andrei Alexandrescu.


    Enfin, pour le problème des gestionnaires, peux-tu en avoir plusieurs ? Ou font-ils toujours référence aux mêmes éléments ?

  12. #12
    sas
    sas est déconnecté
    Membre éprouvé

    Profil pro
    Inscrit en
    Novembre 2003
    Messages
    54
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2003
    Messages : 54
    Points : 1 257
    Points
    1 257
    Par défaut
    Citation Envoyé par Luc Hermitte
    Enfin, pour le problème des gestionnaires, peux-tu en avoir plusieurs ? Ou font-ils toujours référence aux mêmes éléments ?
    si on est sur la même longueur d'onde, voici ma réponse :
    pour mon "getProduitList(const CProduit& param)", il retournera une liste différentes à chaque fois que les membres de CProduit seront assigner ou non.

    ex:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
        CProduit prod_;
        prod_.setCategorie("Disque Dur");
     
        CProduiList prod_list = MyDataBase.getProduitList(prod_); // me retourne la liste des produits appaternant à la catégorie 'Disque Dur"
     
        prod_.setQuantite(0);
        prod_list = MyDataBase.getProduitList(prod_);  // me retournera  la liste des produits appaternant à la catégorie 'Disque Dur" dont le stock est vide
    sinon peux-tu être plus explicite ?

  13. #13
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 279
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 279
    Points : 11 015
    Points
    11 015
    Par défaut
    Ok. Il te faut créer la liste à la volée en retournant ta nouvelle liste par valeur.
    Et si vraiment c'est lent, tu pourras corriger tes listes pour leur donner une sémantique de déplacement, ou de partage du buffer interne.

  14. #14
    Membre averti Avatar de Higestromm
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    516
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 516
    Points : 412
    Points
    412
    Par défaut
    Essai d'utiliser des SmartPointers plutot que des pointeurs si tu peux... pas besoin de libérer explicitement la mémoire comme ca.

  15. #15
    sas
    sas est déconnecté
    Membre éprouvé

    Profil pro
    Inscrit en
    Novembre 2003
    Messages
    54
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2003
    Messages : 54
    Points : 1 257
    Points
    1 257
    Par défaut
    merci à vous tous pour toutes ces infos.

    J'ai survolé quelques docs sur les smartpointers, ça m'a l'air pas mal comme conception. Mais pour l'instant je vais rester sur le retour par valeur car il faut aussi que je me documente sur MySql++ et gtkmm pour la conception de mon soft. Après ça, il est clair que je m'occcuperais de l'optimisation.

  16. #16
    Membre actif
    Profil pro
    Inscrit en
    Août 2003
    Messages
    247
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2003
    Messages : 247
    Points : 276
    Points
    276
    Par défaut
    Les différentes sortes de pointeurs évolués ne sont pas là pour éviter au programmeur d'écrire ses delete, mais pour résoudre les problèmes de fuites de mémoire (exemple: ressource partagée, le dernier utilisateur doit libérer, mais comment savoir si on est le dernier ?).

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

Discussions similaires

  1. Réponses: 5
    Dernier message: 12/05/2009, 16h24
  2. Réponses: 4
    Dernier message: 01/08/2008, 17h13
  3. [POO] Passage d'objet par référence
    Par PinGu- dans le forum Langage
    Réponses: 2
    Dernier message: 10/03/2008, 10h23
  4. [POO] Modifier attributs d'un objet par référence
    Par justSam dans le forum Langage
    Réponses: 8
    Dernier message: 22/02/2007, 07h58
  5. [JACOB] Comment passer un objet par référence à une méthode
    Par zlavock dans le forum Entrée/Sortie
    Réponses: 4
    Dernier message: 21/03/2005, 18h28

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