salut,
pourquoi implémenter le constructeur de copie en utilisant la méthode swap ? versus une simple affectation ?
Merci.
salut,
pourquoi implémenter le constructeur de copie en utilisant la méthode swap ? versus une simple affectation ?
Merci.
Bonjour,
Ça n'existe pas le "SWAP IDIOM COPY CONSTRUCTOR".
L'idiome copy and swap est utilisé pour l'opérateur d'affectation pas pour le constructeur de copie.
Pourquoi quoi ?
Pourquoi on n'utilise pas l'idiome copy and swap pour le constructeur de copie ?
Parce que ça ne sert à rien. En cas d'exception, l'objet n'est tout simplement pas construit, il n'y a donc pas de précaution particulière à prendre pour conserver l'objet (qui n'existera pas) dans l'état initial ou dans un état cohérent.
En outre, que veux tu échanger puisque l'objet n'a pas d'existence (on est en train de le construire).
ok je reformule
pourquoi utilise t-on swap plutôt qu'une bête affectation.
je peux le formuler également comme ceci :
pourquoi
tmp=a
a=b
b=tmp
plutot que
a=b
pourquoi utilise t-on swap plutôt qu'une bête affectation.
Mais justement on ne le fait pas !
Il n'y a pas besoin de swap dans un constructeur par copie. Comme l'a dit gl l'objet n'est même pas encore construit, avec quoi ferait-on des échanges ?
Parce que avec le copy & swap il est beaucoup, *beaucoup* plus simple de coder un opérateur d'affectation correct (et en particulier qui donne de bonnes garanties face aux exceptions).
Mais attention :
1) Dans l'idiome copy-and-swap on n'utilise jamais le swap bête par défaut. Il faut rajouter à la classe une fonction swap un peu plus intelligente qui va swapper les parties internes à la classe.
2) il ne faut pas employer le copy-and-swap à tout bout de champ, en fait son utilisation est même plutôt rare. On n'utilise *pas* le copy-and-swap dans les cas suivants
- Si la classe n'est pas copiable.
Ça peut paraître évident, mais perso vu que la plupart de mes classes sont des entités je désactive presque tout le temps la copie.
- Si la classe ne contient comme données membres que des types de base (int, float, double...). Pas besoin de coder l'op=, celui généré par défaut suffit.
- Si la classe ne contient comme données membres que des types qui savent se copier eux-même (c.a.d dont le copy ctor et l'op= est définit, comme par exemple std::string, std::map, std::shared_ptr...). Pas besoin non plus de coder l'op=, celui généré par défaut est là aussi suffisant
Finalement le copy-and-swap n'est généralement utile que si la classe est copiable, qu'elle possède des pointeurs nus sur des ressources, et que pour X raisons on ne peut pas encapsuler ces pointeurs nus dans des smarts pointeurs.
Donc c'est pas souvent mais quand on se retrouve dans ce cas le copy-and-swap est une bénédiction. Par exemple, je reprends l'exemple donné dans cet article éclairant. On a une classe TFoo toute bête :
Si l'on ne peut pas encapsuler les deux pointeurs dans des shared_ptr et si l'on ne veut pas utiliser le copy-and-swap, alors coder un op= correct pour cette classe tourne vite au cauchemar. L'article détaille un par un tous les pièges à éviter et fini par obtenir l'horreur (correcte!) suivante
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... }
Ça m'étonnerais que beaucoup de programmeurs C++ arrivent à coder un op= comme celui-ci du premier coup ! Il faut une concentration extraordinaire. Alors qu'avec un petit coup d'idiome copy-and-swap, il suffit de définir une fonction swap très simple qui échange les deux pointeurs bar1 et bar2 et l'opérateur = devient simplissime à coder tout en étant correct même en cas d'exception :
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; }
Donc au final je dirais que le principal intérêt du copy-and-swap c'est d'économiser du temps de cerveau disponible.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 void TFoo::swap(const TFoo& that) { TSuperFoo::swap(that); std::swap(fBar1, that.fBar1); std::swap(fBar2, that.fBar2); } TFoo& TFoo::operator=(const TFoo& that) { TFoo tmp(that); this->swap(tmp); return *this; }![]()
Partager