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 :

Constructeur de copie, et opérateur d'affectation.


Sujet :

C++

  1. #21
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par bruno_pages Voir le message
    c'est bien là le problème, car il s'appliquera aussi aux membres ou l'affectation est triviale comme les nombres, d'où la lourdeur et l'inefficacité de la chose
    Je pense que nous sommes d'accord que l'idiome copy&swap n'a un intérêt profond que quand la classe gère des ressources qui peuvent entraîner l'échec de l'assignation mais permettent un swap garanti sans échec. Dans ce cas, copy&swap donne un moyen systématique d'avoir une assignation transactionnelle: ou elle n'a rien change, ou elle a réussit.

    Si il n'y a qu'une ressource, il est souvent possible de la gérer correctement sans passer par l'idiome du copy&swap avec un code assez simple -- même si arriver au code et se convaincre de sa correction ne l'est pas toujours autant, surtout la première fois. Avec plus d'une ressource, ce l'est moins. Faire un constructeur de copie correct et une fonction swap correcte est beaucoup plus simple.

    Ce qui ne veut pas dire que l'utilisation de copy&swap doit être systématique. Faire intervenir l'aspect performance pour les autres membres ne gérant pas de ressources me semble un peu léger -- les copier une fois inutilement est très largement inférieur au temps nécessaire pour la gestion des ressources, ce gain ne va convaincre que les microoptimiseur compulsifs d'utiliser un code plus complexe et plus fragile.

    Faire intervenir les performances pour la gestion des ressources me semble un critere plus pertinent. L'idiome copy&swap par sa nature ne permet pas de réutiliser les ressources déjà allouées et force leur de-allocation et l'allocation de nouvelles. Ça c'est suffisamment coûteux pour justifier un code plus complexe pour l'éviter. Sans parler du fait que dans le cas de ressources rares, le besoin plus grand en ressources de copy&swap peut entraîner l'échec de l'assignation alors que la réutilisation des ressources déjà allouée aurait permis le succès.
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  2. #22
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Hmm, pour ma part le copy&swap est avant tout un moyen pragmatique pour éviter de se tromper en écrivant l'opérateur= pour les classes gérant des ressources (des pointeurs).

    Si je reprend l'exemple donné dans cet article :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    class TFoo : public TSuperFoo {
        TBar* fBar1;
        TBar* fBar2;
        // various method definitions go here...
    }
    L'écriture d'un opérateur = correct, exception-safe et tout le tralala demande une concentration hors du commun. Selon l'article, pour couvrir absolument tous les cas de figure, il faudrait écrire :
    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
     
    TFoo&
    TFoo::operator=(const TFoo& that)
    {
        if (this != &that) {
            TBar* bar1 = 0;
            TBar* bar2 = 0;
     
            try {
                bar1 = new TBar(*that.fBar1);
                bar2 = new TBar(*that.fBar2);
            }
            catch (...) {
                delete bar1;
                delete bar2;
                throw;
            }
     
            TSuperFoo::operator=(that);
            delete fBar1;
            fBar1 = bar1;
            delete fBar2;
            fBar2 = bar2;
        }
        return *this;
    }

    Alors qu'on peut obtenir la même qualité avec un copy and swap de trois lignes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    TFoo& TFoo::operator=(const TFoo& that)
    {
        TFoo temp(that);
        this->swap(temp);
        return this;   
    }

  3. #23
    Membre éprouvé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Points : 937
    Points
    937
    Par défaut
    Tout le tralala est déplacé dans la méthode swap(), qu'il faudra écrire du coup, faut pas l'oublier.

  4. #24
    Membre chevronné
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Points : 1 921
    Points
    1 921
    Par défaut
    ouais suaf que dans le swap t'as pas ce tralalala de try catch

  5. #25
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Citation Envoyé par camboui Voir le message
    Tout le tralala est déplacé dans la méthode swap(), qu'il faudra écrire du coup, faut pas l'oublier.
    Swap est garantie nothrow donc non y'a pas le tralala...


    A bruno_pages : je crois que tu as mal compris ce que j'essayais de dire, je te dis qu'il faut swapper chaque ressource, je dis que l'idiome est important dans le cas de ressource lourde, et afin de garantir l'exception safety de ton code. Ce qui serait beaucoup plus lourd à gérer avec la façon de faire que tu nous montre. (cf le code d'Arzar). Et oui c'est plus lourd (enfin, deux affectations, on a vu pire, c'est pas là que l'optimisation se fera en premier) mais ça offre des garanties beaucoup plus fortes.
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  6. #26
    Modérateur
    Avatar de bruno_pages
    Homme Profil pro
    ingénieur informaticien à la retraite
    Inscrit en
    Juin 2005
    Messages
    3 533
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 64
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : ingénieur informaticien à la retraite
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Juin 2005
    Messages : 3 533
    Points : 6 709
    Points
    6 709
    Par défaut
    ouais suaf qu'il ne faut pas non plus mettre de try catch dans operator= sans swap : si il n'y a plus de mémoire dispo de toute façon on est mal et cela y compris après le try catch, et si il y a une erreur dans le copy contructeur qui mérite d'être récupérée alors on retrouve exactement le même problème avec l'operator= utilisant swap qu'elle que soit son implémentation (param par valeur => copie faite lors de l'appel, param par const ref avec la copie faite dans la définition du constructeur)

    donc le coup du try catch est fallacieux
    Bruno Pagès, auteur de Bouml (freeware), mes tutoriels sur DVP (vieux, non à jour )

    N'oubliez pas de consulter les FAQ UML et les cours et tutoriels UML

  7. #27
    Membre chevronné
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Points : 1 921
    Points
    1 921
    Par défaut
    T'as lu l'article en question ou bien tu trolles ?
    Tu as fondamentalement besoin des try catch et du unwind des constructions partielles pour garantir que ton operator= est exception safe.

  8. #28
    Modérateur
    Avatar de bruno_pages
    Homme Profil pro
    ingénieur informaticien à la retraite
    Inscrit en
    Juin 2005
    Messages
    3 533
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 64
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : ingénieur informaticien à la retraite
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Juin 2005
    Messages : 3 533
    Points : 6 709
    Points
    6 709
    Par défaut
    je suis d'accord sur le fait que la discussion vire au troll, d'ailleurs j'avais en fait mis une remarque à ce sujet mais l'avais enlevée avant de valider

    concernant le try catch, qu'il puisse y avoir une erreur 'applicative' provoquant une exception lors de la création (y compris par copie) est une aberration vu le nombre de constructions avec copie ou non, pour des raisons de conversion ou non, etc produite de façon implicite par le compilateur
    Bruno Pagès, auteur de Bouml (freeware), mes tutoriels sur DVP (vieux, non à jour )

    N'oubliez pas de consulter les FAQ UML et les cours et tutoriels UML

  9. #29
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Citation Envoyé par camboui Voir le message
    Tout le tralala est déplacé dans la méthode swap(), qu'il faudra écrire du coup, faut pas l'oublier.
    Ben non ?
    La méthode swap s'écrit comme d'habitude :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    TFoo::swap(const TFoo& that)
    {   
       TSuperFoo::swap(that);
       std::swap(this->fBar1, that.fBar1);
       std::swap(this->fBar2, that.fBar2);
    }
    Par contre, je suis plus trop sûr de la syntaxe pour appeler le swap de la classe mère

  10. #30
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par bruno_pages Voir le message
    concernant le try catch, qu'il puisse y avoir une erreur 'applicative' provoquant une exception lors de la création (y compris par copie) est une aberration vu le nombre de constructions avec copie ou non, pour des raisons de conversion ou non, etc produite de façon implicite par le compilateur
    Si l'opérateur d'assignation généré par le compilateur n'est pas correct a cause du risque d'exception, c'est un bug de continuer a utiliser celui-la. Tout comme c'est un bug d'utiliser l'opérateur généré par le compilateur s'il induit des fuites de mémoire ou des double delete.
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  11. #31
    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 612
    Points
    30 612
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Par contre, je suis plus trop sûr de la syntaxe pour appeler le swap de la classe mère
    Normalement, tu devrais avoir une fonction swap dans la classe mère, accessible aux classes dérivées (AKA: publique ou protégée), que tu appelle explictement depuis la fonction swap de la classe dérivée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void MaClass::swap(MaClass & rhs)
    {
        Base::swap(rhs);
        /* swap des autres membres */
    }
    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. #32
    Membre du Club
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    57
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 57
    Points : 53
    Points
    53
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Par contre, dés le moment où l'on définit l'une des trois dernières fonctions (constructeur par copie, opérateur d'affectation ou destructeur), il faudra envisager de respecter la forme canonique de Coplien et définirad minima les deux autres (et soit un constructeur trivial, soit un constructeur prenant un (des) argument(s) ), du moins pour les classe ayant sémantique de valeur (pour les classes ayant sémantique d'entité, nous aurons généralement tendance à les déclarer privé et à ne pas les définir, de manière à interdire la copie et l'affectation...
    Salut. Que recouvre semantique d'entite ?

    en attendant C++11 et le nouveau sens que la norme donne au mot clé delete )
    Qui pourrait etre quoi ?

  13. #33
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par mailaka Voir le message
    Salut. Que recouvre semantique d'entite ?
    En deux lignes: une classe ayant une semantique de valeur est telle qu'utiliser une variable ou un autre resultant d'une copie ne change rien au resultat (on parle aussi de classe reguliere). Un classe n'ayant pas une semantique de valeur a une semantique d'entite: l'objet a autant d'importance que sa "valeur" (on parle plutot d'etat dans ce cas).

    La quasi-totalite des classes de la SL a une semantique de valeur, parmi les exceptions on a les flux.

    Qui pourrait etre quoi ?
    On peut utiliser = delete pour indiquer que des membres normalement genere par les compilateur ne doivent pas l'etre. (Ca permet de donner un message d'erreur immediat pour toutes les tentatives d'utilisation, au contraire de la technique suggeree par koala01 qui peut retarder l'erreur jusqu'a l'edition de liens).
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  14. #34
    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 612
    Points
    30 612
    Par défaut
    Citation Envoyé par mailaka Voir le message
    Qui pourrait etre quoi ?
    En fait, c'est déjà clairement défini. (cf la réponse de Jean Marc)

    Ce qu'il y a, c'est:
    • que la prochaine norme n'est pas encore officiellement sortie
    • que les compilateur qui supportent déjà cette norme nécessitent de préciser explicitement que nous voulons la suivre
    • que c'est une "feature" toute neuve, qui sera purement et simplement refusée si l'utilisation de C++11 n'est pas activé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

  15. #35
    Invité
    Invité(e)
    Par défaut
    Bah ça alors, je vois que c'est partit en délire total... Plus sérieusement, j'ai commencé à lire, mais je me suis vite fait largué. Je répond au premier post :
    Citation Envoyé par Arzar Voir le message
    Comme son nom l'indique, le constructeur par copie construit un nouvel objet, alors que l'opérateur d'affectation opère sur un objet existant.

    Par exemple, un quiz classique : Qu'affiche chacune des lignes suivantes ?
    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
     
    #include <iostream>
    struct MaClasse
    {
       MaClasse(){std::cout << "Default Ctor\n"; }
       MaClasse(const MaClasse&){std::cout << "Copy Ctor\n"; }
       MaClasse& operator=(const MaClasse&){std::cout << "Op = \n"; return *this;}
    };
     
    int main()
    {
       MaClasse m1;//Affiche : Default Ctor
     
       MaClasse m2(m1);//Affiche : Copy Ctor
       MaClasse m3 = m2;//Affiche : Op = 
       MaClasse m4 = MaClasse();//Affiche : Default Ctor
     
       m1 = m2;//Affiche : Op = 
    //J'ai pas testé, mais je pense que c'est ça...
    }
    Voilà, j'ai pas tester, mais je pense que c'est ça. Ensuite, certains parle de copy&swap, c'est quoi au juste ?

  16. #36
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par Abdelite Voir le message
    Voilà, j'ai pas tester, mais je pense que c'est ça. Ensuite, certains parle de copy&swap, c'est quoi au juste ?
    C'est un idiome permettant d'implementer l'operateur d'assignation en utilisant une fonction realisant un swap (cette fonction doit avoir ete implementee a part, il ne faut pas utiliser std::swap qui utilise l'assignation ).

    L'assignation se resume alors a
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    Foo& operator=(Foo f)
    {
       swap(*this, f);
       return *this;
    }
    (La copie est dans le fait qu'on passe par valeur -- faire ainsi a l'avantage que le compilateur peut parfois elider une copie quand on assigne un temporaire -- le resultat d'une fonction par exemple).

    Cet idiome a l'avantage d'etre une maniere systematique d'ecriture un operateur d'assignation robuste en cas d'exception pour autant qu'on dispose d'un swap (voir ma note sur std::swap) n'en jetant pas (si on n'est pas capable d'ecrire un swap qui ne jette pas d'exception, ecrire une assignation robuste ne va pas etre simple et on peut se demander l'interet de fournir ces operations a la classe) -- soit l'operation reussit, soit l'objet que l'on cherche a modifie ne l'a pas ete.
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  17. #37
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par Jean-Marc.Bourguet Voir le message
    C'est un idiome permettant d'implementer l'operateur d'assignation en utilisant une fonction realisant un swap (cette fonction doit avoir ete implementee a part, il ne faut pas utiliser std::swap qui utilise l'assignation ).
    Un idiome ?
    Un swap, c'est bien un échange de valeurs entre deux classes n'est-ce pas ?

    Citation Envoyé par Jean-Marc.Bourguet Voir le message
    (La copie est dans le fait qu'on passe par valeur -- faire ainsi a l'avantage que le compilateur peut parfois elider une copie quand on assigne un temporaire -- le resultat d'une fonction par exemple).
    Je n'ai pas très bien compris... Tu entends quoi par élider ?

    Vraiment désolé, je sais que j'ai un niveau très déplorable en C++, mais je ne demande qu'à m'améliorer. Essayer d'utiliser des termes très basique pour que je comprenne bien, merci à vous

  18. #38
    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 612
    Points
    30 612
    Par défaut
    Un idiome pourrait être considéré comme "une manière de faire" propre à un langage donné, permettant d'assurer à la fois une implémentation "simple" (parce que répétée sans arrêt) et sécurisante (parce que, justement, on l'implémente souvent, et que, en plus, il est possible de prendre une série de problème en compte qu'il serait difficile de cumuler si on n'utilisait pas l'idiôme).
    Un swap, c'est bien un échange de valeurs entre deux classes n'est-ce pas ?
    C'est bien cela...

    Élider: éviter, supprimer ce qui est "inutile"
    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

  19. #39
    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 Abdelite Voir le message
    Un idiome ?
    En simple, une recette de cuisine. Une façon de faire quelque chose.

    Un swap, c'est bien un échange de valeurs entre deux classes n'est-ce pas ?
    oui

    Tu entends quoi par élider ?
    éviter.
    En gros le compilateur va optimiser en enlevant des copie inutile du code.

    Citation Envoyé par Abdelite Voir le message
    Vraiment désolé, je sais que j'ai un niveau très déplorable en C++, mais je ne demande qu'à m'améliorer.
    Aucun problème

  20. #40
    Invité
    Invité(e)
    Par défaut
    Merci, pour en revenir à la méthode swap, je la définie moi-même, ou j'utilise std::swap ?

    (La copie est dans le fait qu'on passe par valeur -- faire ainsi a l'avantage que le compilateur peut parfois elider une copie quand on assigne un temporaire -- le resultat d'une fonction par exemple).
    Dans ce cas, il ne faudrait pas utiliser utiliser un pointeur ou une référence en paramètre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Foo& operator=(Foo &f)
    {
       swap(*this, f);
       return *this;
    }

Discussions similaires

  1. Réponses: 1
    Dernier message: 03/02/2015, 15h21
  2. Réponses: 7
    Dernier message: 17/08/2014, 15h20
  3. Réponses: 96
    Dernier message: 08/04/2013, 11h54
  4. Constructeur de copie et Template: Transtypage
    Par ikkyu_os dans le forum Langage
    Réponses: 9
    Dernier message: 26/12/2004, 22h29

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