opérateur =

opérateur = - C++ - Programmation

Marsh Posté le 07-03-2009 à 14:10:15    

Une question concernant la bonne manière décrire l'opérateur d'assignement dans une classe
 

Code :
  1. MaClasse& MaClasse::operator =(const MaClasse& Other)
  2. {
  3.    
  4.     MaClasse Temp(Other);
  5.     std::swap(Temp.Ressource, this->Ressource);
  6.     return *this;
  7. }


 
à priorie ce code est un bon exemple de comment faire la chose, mais je me demandais pourquoi utiliser swap au lieu de faire un = ? merci

Reply

Marsh Posté le 07-03-2009 à 14:10:15   

Reply

Marsh Posté le 07-03-2009 à 16:04:37    

bof, ça dépend des classes.
Le swap c'est pas ce qu'il y a de plus simple ni de plus optimisé, si un simple égal marche, mieux vaut s'en servir. La dépend de la sémentique de la classe.
Noublie pas que le compilateur génère par défaut un opérateur= qui copie membre à membre (et pas bit-à-bit contrairement à ce que tu peux lire sur qlq mauvais sites) et que souvent ça suffit. Enfin, il y a des classes qui ne sont pas copiables/assignables car ça n'a pas de sens. Les classes noncopyable pour reprendre le terme de boost.

Reply

Marsh Posté le 07-03-2009 à 16:28:29    

weblook$$ a écrit :

Une question concernant la bonne manière décrire
l'opérateur d'assignement dans une classe
 

Code :
  1. MaClasse& MaClasse::operator =(const MaClasse& Other)
  2. {
  3.    
  4.     MaClasse Temp(Other);
  5.     std::swap(Temp.Ressource, this->Ressource);
  6.     return *this;
  7. }


 
à priorie ce code est un bon exemple de comment faire la chose,


 
Le dernier idiome à la mode est

Code :
  1. MaClasse& MaClasse::operator=(MaClasse Other)
  2. {
  3.    swap(*this, Other);
  4.    return *this;
  5. }


En ayant défini une fonction swap dans le même namespace que MaClasse.  Il
y a eu d'autres idiomes à la mode basé sur swap également (celui-ci permet
au compilateur d'enlever la copie dans le cas où on assigne un temporaire).
 

Citation :

mais je me demandais pourquoi utiliser swap au lieu de faire un
=?  merci


 
Les idiomes utilisant swap (sur le même type plutôt qu'à la main comme tu
le fais, en ayant défini une fonction swap ou en ayant spécialisé
std::swap) pour définir operator=, c'est basé sur l'hypothèse que swap ne
jette pas d'exception.  Sous cette hypothèse, c'est une technique
systématique qui s'assure que même s'il faut faire des choses capables de
jeter une exception -- dont allouer de la mémoire --, elles auront lieu
avant le swap et donc on se retrouve avec une "exception safety" forte.
 
Elle n'est pas toujours sans inconvéniants, mais ceux-ci sont relativement
mineurs par rapports aux avantages.  Elle me semble une bonne manière
d'agir tant qu'on n'a pas des bonnes raisons de faire autrement.
 
- il faut écrire une fonction swap -- mais son contenu est aussi
  systématique et pas fondamentalement plus compliqué que ce qu'on écrirait
  dans l'operator=
 
- il faut que le constructeur de copie n'appelle pas l'assignation. Il
  y a des gens qui font

Code :
  1. MaClasse::MaClasse(MaClasse const& Other)
  2. {
  3.    *this = Other;
  4. }


  pour éviter la duplication de code.  Mais cette technique a des
  désavantages en soi qui font que l'éviter n'est pas nécessairement une
  mauvaise idée.
 
- on ne peut pas récupérer ce qui a déjà été alloué pour this et donc on
  peut avoir une consommation supérieure de mémoire.  Si c'est important,
  rien n'oblige à utiliser une technique à base de swap, c'est pas la seule
  manière d'optenir une sémantique de rollback.  Sans elle, il n'y a guère
  qu'une analyse cas par cas.

Message cité 1 fois
Message édité par Un Programmeur le 07-03-2009 à 17:05:36
Reply

Marsh Posté le 07-03-2009 à 16:57:27    

Un Programmeur a écrit :

 

Le dernier idiome à la mode est

Code :
  1. MaClasse& MaClasse::operator=(MaClasse Other)
  2. {
  3.    using std::swap;
  4.    swap(*this, Other);
  5.    return *this;
 


 

Merci pour ta réponse détaillée. Un point m'échappe..
La fonction swap utilisée ici est bien celle de la std? tu précises qu'il convient d'en définir une soit même ?
Comment est ce que le code ci-dessus peut appeler cette fonction swap plutot que celle
de la std , puisque tu fais explicitement un using std::swap just au dessus..


Message édité par weblook$$ le 07-03-2009 à 16:59:51
Reply

Marsh Posté le 07-03-2009 à 17:05:09    

Si tu utilises std::swap et que tu ne l'as pas spécialisé, n'utilises pas cet idiome au risque de te retrouver avec une boucle infinie: std::swap est défini en fonction de l'opérateur =.
 
Pour comment est-ce que la bonne version est trouvée: argument dependant lookup (alias Koenig's Lookup) + résolution de surcharge (qui donne priorité aux fonctions par rappor aux fonctions templates).
 
En fait, dans ce contexte le using n'est pas nécessaire (il l'est quand on écrit des templates et qu'on veut utiliser le swap trouvé par l'ADL s'il y en a un ou celui de std:: s'il n'y en a pas; je corrige mon message).

Reply

Marsh Posté le 07-03-2009 à 18:23:06    

ce qui me dérange également ,c'est l'utilité de faire un swap, on s'en fiche que other contienne this ? on veut juste que this contienne other, alors pourquoi swaper?? c'est simplement le nom qui induit en erreur en réalité on ne swappe pas ?

Reply

Marsh Posté le 07-03-2009 à 18:33:20    

Il est passé par valeur.  Son contenu est détruit à la fin de l'opérateur.

Reply

Marsh Posté le 07-03-2009 à 18:36:37    

mais il y a donc des opérations inutiles, il aurait suffit d'affecter plutot que de swapper


Message édité par weblook$$ le 07-03-2009 à 18:37:19
Reply

Marsh Posté le 07-03-2009 à 18:43:05    

Mesures et quand tu trouves un cas où la différence est significative, tu passes à d'autres techniques.

 

Le gros avantage de cet idiome, c'est qu'il te donne par construction un comportement correct, même quand il peut y avoir des exceptions.  D'autres méthodes te donne le même résultat, mais ne sont pas systématiquement applicable.  Et parfois tu n'as pas besoin du rollback en cas d'exception.  Mais il me semble que c'est une bonne technique à utiliser tant qu'on n'a pas de bonnes raisons pour faire autrement.


Message édité par Un Programmeur le 07-03-2009 à 18:46:03
Reply

Marsh Posté le 07-03-2009 à 19:00:36    

ok thx

Reply

Marsh Posté le 07-03-2009 à 19:00:36   

Reply

Marsh Posté le 07-03-2009 à 22:30:34    

l'operatro= definit par swap est relativement sexy et le sera d'autant plus lorsqu'on aura un vrai semantique de move avec les rvalue references

Reply

Marsh Posté le 08-03-2009 à 23:13:46    

Finalement je me rends compte que je n'arrive pas avoir le cas ou la non spécialisation de l'opérateur = peut poser problème...
Dans quel  cas y a t-il le même problème qu'on retrouve avec le constructeur de recopie (lorsque qu'un membre par exemple est un pointeur) ?

 

L'opérateur = n'est appelé que lorsqu'une fonction retourne un Objet de la classe

 

A& func(A& a)
{
return a;
}

 

f=func(f1); //appelé ici

 

Mais étant donné qu'il s'agit d'une référence qui est retourné, il n'y pas le problème d'un pointeur non valide

 

c'est utile uniquement dans le cas où l'on écrit :

 

A a;
A a1;
a=a1;

 

?

 

bref j'aurais besoin d'un petit éclairssissement svp

 


Message édité par weblook$$ le 08-03-2009 à 23:27:18
Reply

Marsh Posté le 09-03-2009 à 09:35:54    

Qui va detruire l'objet pointe si tu copies un pointeur?

Reply

Marsh Posté le 09-03-2009 à 11:45:53    

oups oui, en me levant ce matin tout était plus clair... :$, effectivement dans le cas du retour de fonction, on aura bien in fine deux objets ayant chacun un membre pointant vers la même adresse mémoire donc problème lors de la destruction car double delete sur une même adresse, et si de plus on alloue ce membre dans le constructeur, on aura aussi une fuite de mémoire, car l'adresse mémoire initiale du membre donné de type pointeur appartenant à l'objet recevant le retour de fonction ne sera jamais désaoullé, enfin si j'ai bien tout compris !
 
 
Mais j'ai encore un point que je ne comprends pas, dans l'exemple ci-dessous je passe l'argument par référence et non par valeur, et tout semble Ok
 

Code :
  1. struct Bar
  2. {
  3. int * p;
  4. Bar():p(new int(2)){}
  5. Bar& operator =(const Bar& f) //REF
  6. {  
  7.   std::swap(*p,*f.p);
  8.   return *this;   
  9. }
  10. ~Bar()
  11. {
  12.  delete p;
  13. }
  14. };
  15. int main()
  16. {
  17. Bar a, b;
  18. *a.p=3;
  19. b=a;
  20. system("pause" );
  21. }


Message édité par weblook$$ le 09-03-2009 à 12:07:06
Reply

Marsh Posté le 09-03-2009 à 11:50:09    

Bon j'enchaine lol, j'ai également rencontré un problème en utilisant le fameux std::swap, dans le code suivant :

 
Code :
  1. #include <iostream>
  2. #include <algorithm>
  3. struct Bar
  4. {
  5. int nb;
  6. Bar():nb(4){}
  7. Bar& operator =(const Bar f)
  8. {  
  9.   std::swap(nb,f.nb); //NOK
  10.   return *this;   
  11. }
  12. };
  13. int main()
  14. {
  15. int nb,n;
  16. std::swap(nb,n); //OK
  17. system("pause" );
  18. }
 


Le swap dans l'operator = ne passe pas, le compilateur me dit (Visual 08), void std::swap(_Ty &,_Ty & )' : paramètre modèle '_Ty' ambigu

 

:??:


Message édité par weblook$$ le 09-03-2009 à 11:51:21
Reply

Marsh Posté le 09-03-2009 à 20:14:51    

ok c'est le const qui foutait le bordel... mais un const_cast<int>(f.nb) ne marche pas , ça me suprend.. obliger de fire un static_cast, à moins de faire

 
Code :
  1. std::swap<int>(nb,*const_cast<int*>(&f.nb));


Message édité par weblook$$ le 09-03-2009 à 20:17:56
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed