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 et new


Sujet :

C++

  1. #1
    Candidat au Club
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    2
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 2
    Par défaut retour par référence et new
    Bonjour,
    Étant novice dans l'utilisation du retour par référence je me demande ce qui suit:
    j'ai une classe (Point3D qui comporte les variables x,y et z ) dont une méthode (ci-bas) effectue un retour par référence d'un Vecteur3D (autre classe). Ma question, dois-je utiliser "delete" comme je le fais actuellement? Merci.


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // calcules le vecteur entre le point actuel et le point P
    Vecteur3D & Point3D::operator - ( Point3D P )
    {
       Vecteur3D * Q = new Vecteur3D();
     
        (*Q).x = x - P.x;
        (*Q).y = y - P.y;
        (*Q).z = z - P.z;
        return *Q;
        delete Q;
    }

  2. #2
    Membre chevronné
    Avatar de myzu69
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2007
    Messages
    220
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2007
    Messages : 220
    Par défaut
    Salut,

    je ne comprends pas bien ce que tu veux faire là mais il y a visiblement des problèmes ...

    Pourquoi retourner un Vecteur3D quand tu soustrait deux Point3D ? Je vois bien qu'un vecteur est calculé en soustrayant les coordonnées des deux points extrémités mais j'ai du mal à voir les différences, le code des deux classes est-il différent pour devoir séparer les deux ?

    En admettant que la classe Vecteur3D soit nécessaire, pourquoi ne pas avoir créé un constructeur qui prend en paramètres les valeurs en x, y et z ?

    Enfin pour répondre à ta question, je ne comprends pas pourquoi tu crée un Vecteur3D dynamique, avec ton code, le delete ne sera jamais atteint et l'objet ne sera pas supprimé. Pourquoi vouloir absolument retourner une référence ? Créer un objet non dynamique et le renvoyer ne convient-il pas ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    // calcule le vecteur entre le point actuel et le point P
    Vecteur3D Point3D::operator- (const Point3D & P)
    {
         Vecteur3D Q(x - P.x, y - P.y, z - P.z);
         return Q; // on retourne une copie de Q
    }
    On peut faire ça en 1 ligne mais bon, c'est juste un exemple ...

  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, et bienvenue sur le forum.

    D'abord, il faut savoir que, le delete apparaissant après l'instruction de retour, il ne sera jamais effectué (un compilateur "correctement réglé" t'aurait sans doute lancé un avertissement du genre "attention, cette instruction ne sera jamais exécutée" )

    Cela provoquera donc, au mieux, une fuite mémoie

    Il serait par contreencore pis d'inverser l'ordre sous la forme de
    parce que tu renverrais alors une référence sur... un objet qui a déjà été détruit (ce qui reviendrait donc au même que si tu n'avais simplement pas eu recours à l'allocation dynamique )

    Ensuite, il faut savoir que l'opérateur - ne devrait pas renvoyer une référence, mais un nouvel objet...

    Les seuls opérateurs qui renvoient une référence (sur le premier opérande de l'opération) sont ayant une composante d'affectation ( =, +=, -=, *=, /= pour être précis), parce que l'objet résultant est... le premier opérande.

    Ainsi, le plus souvent, nous agirons en deux temps:

    1- définir l'opérateur ayant une composante d'affectation comme fonction membre de la classe, renvoyant "ce qui est pointé par this" par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    Vecteur3D & Vecteur3D::operator -=(Vecteur3D const & v)
    {
        /* calculer la différence directement sur les membres de this */
        x-=v.x;
        y-=v.y;
        /*...*/
        /* renvoyer l'objet modifié */
        return *this;
    }
    2- définir l'opérateur n'ayant pas de composante d'affectation comme fonction libre qui effectue une copie du premier opérande et renvoie le résultat de l'opérateur équivalent ayant composante d'affectation sur cette copie. Par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    Vecteur3D operator-=(Veceur3D const & v1, Vecteur3D const & v2)
    {
        /* copie de v1 */
        Vecteur3D temp(v1);
        return temp-=v2;
    }
    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
    Membre chevronné
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    507
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Mai 2006
    Messages : 507
    Par défaut
    Bonjour,

    Philosophiquement parlant:
    Citation Envoyé par myzu69 Voir le message
    Pourquoi retourner un Vecteur3D quand tu soustrait deux Point3D ? Je vois bien qu'un vecteur est calculé en soustrayant les coordonnées des deux points extrémités mais j'ai du mal à voir les différences, le code des deux classes est-il différent pour devoir séparer les deux ?
    Mathématiquement, un point et un vecteur sont deux choses bien différentes... On peut donc penser qu'un certain nombre de méthodes soient proposées pour une classe mais pas pour l'autre... N'ayant pas le détail de ces classes, je pense qu'on ne peut juger un tel point.

    Informatiquement parlant:
    En admettant que la classe Vecteur3D soit nécessaire, pourquoi ne pas avoir créé un constructeur qui prend en paramètres les valeurs en x, y et z ?
    Un certain nombre d'APIs et de conventions de codage recommande de limiter les paramètres des constructeurs, et de préferer les passages par 'set' ou par des méthodes d'initialisations... Sa manière d'initialiser ses variables n'est donc pas choquante.

    Enfin pour répondre à ta question, je ne comprends pas pourquoi tu crée un Vecteur3D dynamique, avec ton code, le delete ne sera jamais atteint et l'objet ne sera pas supprimé.
    Totalement d'accord, il y a une fuite de mémoire inquiétante...

    Pourquoi vouloir absolument retourner une référence ? Créer un objet non dynamique et le renvoyer ne convient-il pas ?
    Pour jouer l'avocat du diable, renvoyer une copie peut être couteux... On ne sait pas ce que contient sa classe... Bon là, j'avoue qu'a priori il n'y a que trois nombres à copier, mais par principe je trouve cela moyen...

    Autre question de principe (j'en ai beaucoup!), je trouve dangeureux dans une opération de soustraction que le résultat soit de type différent des paramètres d'entrée...
    Si on a que des éléments du même type voir les solutions de koala01 sinon passer par une méthode avec un nom spécifique du type :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Vecteur3D* Point3D::CreerVecteurAvec (const Point3D& P);
    // ou sans doute mieux:
    bool Point3D::CreerVecteurAvec (const Point3D& P, Vecteur& V);
    Finish:
    Citation Envoyé par myzu69 Voir le message
    On peut faire ça en 1 ligne mais bon, c'est juste un exemple ...
    ça c'est pas un argument !

  5. #5
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Bonjour et bienvenu,
    Quelques éléments complémentaires :
    Strictement parlant, référence ou pointeur : il faut faire attention à la durée de vie de l'objet désignée. Si tu retourne un référence ou un pointeur qui est détruit en fin de fonction (soit explicitement avec ta tentative de delete, soit implicitement sur une variable locale à ta fonction), ta référence ou ton pointeur sont invalides et leur utilisation est indéterminée (généralement un crash au bout d'un moment).
    @fallbot : (pour jouer l'avocat du contre-diable ) un retour par valeur avec une fonction comme Vecteur3D Point3D::CreerVecteurAvec (const Point3D& P)const;, avec un peu de chance, sera optimisé en (N)RVO évitant la copie.

  6. #6
    Membre chevronné
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    507
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Mai 2006
    Messages : 507
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    @fallbot : (pour jouer l'avocat du contre-diable ) un retour par valeur avec une fonction comme Vecteur3D Point3D::CreerVecteurAvec (const Point3D& P)const;, avec un peu de chance, sera optimisé en (N)RVO évitant la copie.
    Un peu de rigueur que diable !

  7. #7
    Invité
    Invité(e)
    Par défaut
    Bonjour,
    En ce qui me concerne, je procède de cette façon là

    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
     
    Point3D *P1; // Un pointeur sur un objet existant
    Point3D *P2; 
    // Maintenant, un veut créer un vecteur V
    // le but étant naturellement de l'utiliser, par exemple pour le comparer
    // à un autre vecteur   
      Vecteur3D *V1=new Vecteur3D(P1, P2);
      ... // traitement
      delete V1;
     
    // La classe Vecteur3D
    class Vecteur3D
    {
      float a, b, c;
    public:
      Vecteur3D {Point3D *p1, Point3D *p2)
      {
        a=p2->x - p1->x;
        b=p2->y - p1->y;
        c=p2->z - p1->z;
      }
    };
    Dernière modification par Invité ; 27/05/2010 à 18h05.

  8. #8
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Quel est l'intérêt de l'allocation dynamique ?
    Et quel est l'intérêt de passer les paramètres par pointeur ? En plus il n'y a pas de prise en compte de la constance. Perso, quand je passe un pointeur, c'est que la fonction peut accepter NULL. Si ce n'est pas le cas (et ce n'est pas le cas de ton constructeur), je préfère passer des références (constantes au besoin).

  9. #9
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    Concernant l'allocation dynamique de Vecteur3D, il est vrai que ça n'a pas grand intérêt ici. Je j'ai écrit juste pour justifier l'écriture d'un delete

    Par contre, les paramètre sont des pointeurs, parce que les Point3D sont des pointeurs sur des objets existants, qui ont été crées avec un new (naturellement) et ne seront normalement supprimés qu'en fin d'exécution.
    Toute mon organisation est basée sur cette logique
    1- class TMatricule qui contient un objet de type void et des caractéristiques telle que VISIBLE
    2- les classes Point3D, TLigne et TObjet qui décrivent les objets correspondants. Chaque objet possède un matricule, un matricule peut définir plusieurs objets en fonction de l'historique des modifications
    3- la classe TLigne définit une succession de pointeurs sur matricule qui correspondent à des points (liste chainée).
    etc. etc.

    Suite à nos échanges concernant les passages par référence, il est vrai que pour des cas comparables Borland passe les paramètres par référence. Je m'y suis attelé et j'ai modifié en conséquence. C'était assez fastidieux, il m'en reste probablement encore un peu dans d'autres .h, je les ferai, c'est promi.
    Il se trouve que j'avais un long calcul dont j'avais mesuré le temps d'exécution, mais seulement avec ma trotteuse : 30s. Après modification, j'ai mesuré 28s. Donc la différence n'est pas mesurable.

    Quant à l'aspect pointeur NULL, qui dit que je ne les teste pas dans mes constructeurs? Ce que j'ai écrit dans mon message n'est pas un exemple réel, mais que j'ai tapé pour montrer le principe de la méthode que j'utilisais, sans pour autant dire que c'est la meilleure, ou surtout la seule.

    Cordialement.

  10. #10
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    Je pense qu'une bonne partie de l'implémentation des opérateurs consiste à s'assurer qu'on en viole pas les règles de base:
    • Les opérateurs arithmétiques sont conçus pour marcher sur des valeurs.
    • D'ailleurs, on ne peut pas les redéfinir pour deux pointeurs (mais on peut pour un pointeur et un objet).
    • Les opérateurs arithmétiques ne modifient pas les objets passés en paramètre (a+b ne doit pas avoir l'effet de a+=b) et doivent donc retourner un nouvel objet
    • Le nouvel objet est retourné par valeur également.

    Bien sûr, on peut "tordre" un peu ces règles si l'objet en question est un pointeur intelligent vers le "vrai" objet (ce qui tend à être plus ou moins le cas pour les classes comme std::string, CString, etc.). Mais même dans ce cas, ledit pointeur intelligent est retourné par valeur.

    En règle générale, si un opérateur doit retourner une référence, c'est soit une référence vers l'objet lui-même (*this), soir une référence vers un objet contenu (T& vector<T> :: operator[] (size_t index), etc.)
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  11. #11
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Salut,
    Citation Envoyé par Pierre Dolez Voir le message
    Quant à l'aspect pointeur NULL, qui dit que je ne les teste pas dans mes constructeurs?
    Ce que j'entendais ce n'est pas tester la valeur NULL du pointeur, mais que NULL est une valeur acceptable dans le contrat de la fonction. En d'autres termes, si tu passes NULL la fonction réussie et l'objet ressort avec un état valide. Ca ne peut pas être if(null) -> error.

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

Discussions similaires

  1. Retour par référence sur const
    Par Cheps dans le forum C++
    Réponses: 3
    Dernier message: 14/12/2008, 22h36
  2. Retour par référence d'un pointeur
    Par FunkyTech dans le forum C++
    Réponses: 16
    Dernier message: 22/07/2008, 13h56
  3. retour par référence de l'opérateur ++
    Par BigNic dans le forum C++
    Réponses: 4
    Dernier message: 02/08/2006, 18h35
  4. retour d'objet par référence...
    Par sas dans le forum C++
    Réponses: 15
    Dernier message: 28/05/2005, 17h54

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