opérateur = - C++ - Programmation
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.
Marsh Posté le 07-03-2009 à 16:28:29
weblook$$ a écrit : Une question concernant la bonne manière décrire
|
Le dernier idiome à la mode est
Code :
|
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 |
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 :
|
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.
Marsh Posté le 07-03-2009 à 16:57:27
Un Programmeur a écrit : Le dernier idiome à la mode est
|
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..
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).
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 ?
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.
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
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.
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
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
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 :
|
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 :
|
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
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 :
|
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
à 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