bonjours
je définir un operateur = entre classesmais ça se passe avec mon compilateur!!!Code:
1
2
3
4 classe Point{ Point operator = (Point & P) {return P;} }
Version imprimable
bonjours
je définir un operateur = entre classesmais ça se passe avec mon compilateur!!!Code:
1
2
3
4 classe Point{ Point operator = (Point & P) {return P;} }
"ça se passe avec mon compilateur" ne définit par correctement ton problème.
De plus, ce code est très faux pour un opérateur = de classe. L'idiome Copy-And-Swap est préconisé.
Salut,
Il est déjà beaucoup plus facile de comprendre ce que fait "operator =" lorsque l'on essaye de l'exprimer en bon francais: il s'agit de "l'opératteur d'affectation" ;)
Une fois cette expression faite, tu devrais presque être en mesure, au prix d'un peu de réflexion, de déterminer la manière correcte de le déclarer ;).
Tu peux aussi décider de retenir la manière de le déclarer par coeur, mais, il faut avouer que cela a beaucoup moins de charme :D
Allons y donc une fois pour la réflexion, car elle car elle est vraiment intéressante :D
Nous savons donc qu'il s'agit d'un opérateur d'affectation. Essayons de définir clairement chacun des terme:
- opérateur: pour faire simple, nous pouvons dire qu'il s'agit d'une fonction, même si elle réagit de manière un peu particulière
- affectation: c'est le fait de donner à l'instance courante d'un objet le contenu d'une instance différente
Le fait de parler d'instance courante signifie que nous devons renvoyer... une référence sur un objet, et que l'objet renvoyé doit venir de this.
Pour mémoire, car, pour l'instant nous ne nous inquiétons de la manière correcte de déclarer l'opérateur, cela signifie aussi, étant donné que this est un pointeur qu'il faudra au final renvoyer... ce qui est pointé par this ;)
Il faut aussi s'intéresser à "l'instance différente" que nous voulons assigner à l'instance courante... Elle doit:
- Ne pas être copiée lorsqu'elle est passée comme paramètre à l'opérateur
- Ne pas pouvoir être modifiée par la fonction pour respecter le sacro-saint principe de "const-correctness"
Le (1) implique que l'argument doit être transmis... par référence.
Le (2) implique que l'argument doit être... constant.
au final, nous nous rendons donc compte que l'opérateur d'assignation, que nous désignerons sous le nom de operator= doit renvoyer une référence sur l'objet courent (MonType&) et prendre en argument une référence constante sur l'objet à assigné (MonType const & )
La déclaration correcte sera donc de l'ordre de
Maintenant que la réflexion est faite, tu peux le retenir par coeur :DCode:MonType& operator = (MonType const & );
Il est souvent plus intéressant d'écrire
en fait.Code:MonType& operator = (MonType);
Pour rappel, la FAQ contient déjà ce genre de réponse.
Merci de la consulter.
En fait, c'est, justement parce que l'on décide de passer l'argument par valeur plutôt que par référence qu'une copie de l'objet passé s'effectue...
Il n'y a donc normalement plus qu'à swapper le contenu de l'argument et de l'objet courant ;)
Or, quand on y regarde de plus près, c'est effectivement ce que conseille l'idiome copy and swap: créer une copie de l'objet passé en paramètre et inverser le contenu de cette copie avec l'objet courant ;)
Il y a peut être des raisons qui font qu'il est plus intéressant de directement demander la copie (louffoque, pourrais tu les donner, s'il y en a), mais, personnellement, j'aime autant "garder la main" sur le moment où je fais des copies de mes objets, et je trouve - à titre personnel - plus cohérent de rester sur le principe d'éviter les copies de classes autant que faire se peut...
Ne serait-ce que parce qu'il reste possible que le constructeur par copie lance une exception et que, en demandant explicitement la copie au sein de l'opérateur d'affectation, je me laisse une chance de récupérer (et de gérer le cas échéans) cette exception ;)
Ah mais je suis tout à fait d'accord, et dans mon esprit ça c'est toujours passé comme ça, seulement ce n'est pas la première fois que j'entends l'argument de loufoque, et justement j'aimerais bien entendre les dites raisons qui font que c'est plus intéressant.
Edit : en fait c'est ma ma faute, je voulais parlé justement que du swap (non-throwing swap?) .. En me relisant j'ai compris le pourquoi de ton intervention.
Edit 2 : http://en.wikibooks.org/wiki/More_C%.../Copy-and-swap il est question de l'optimisation dû au passage par valeur dans le cas du copy and swap.
Citation:
Il y a peut être des raisons qui font qu'il est plus intéressant de directement demander la copie (louffoque, pourrais tu les donner, s'il y en a)
Bien sûr, ça ne marche que si le compilo fait de la NRVO.Code:
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
27 #include <iostream> struct A { A() {} A(const A&) { std::cout << "A::A(const A&)" << std::endl; } }; A make_A() { return A(); } void take_A_1(A a) { } void take_A_2(const A& a) { A a2(a); } int main() { take_A_1(make_A()); // zéro copie take_A_2(make_A()); // une copie }
Ah, tiens, oui, réellement intéressant...
Mais il reste la bonne question à se poser qui est de savoir si le compilateur utilise la NRVO, et, surtout, si tous les compilateurs actuels ne le supportent pas, quelle politique est la plus intéressante à appliquer :P
C'est le cas (testé) avec Gcc 4.3.0, et il semble que ce soit le cas pour VC2005...
As-tu une idée en ce qui concerne les autres :question: est-ce, à l'heure actuelle plutôt la règle générale ou plutôt l'exception :question:
Salut,
J'ai envi de dire que dans le pire des cas (le compilo ne fait pas NRVO - Named Return Value Optimization pour les intimes), tu as le même coût qu'un passage par référence et une variable locale temporaire. Alors que si le compilo supporte la NRVO alors tu gagne une copie. Donc j'aurais tendance à conclure : passe par valeur...
Ce qu'on m'avait dit sur les copies, c'était plutôt ça:
Code:
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 class A { private: int a; public: A(int a) : a(a) { } A& operator=(A tmp) { Swap(tmp); return *this; } void Swap(A& other) { std::swap(a, other.a); } }; class B { private: int b; public: B(int b) : b(b) { } B& operator=(B const &src) { B tmp(src); //Copie explicite Swap(tmp); return *this; } void Swap(B& other) { std::swap(b, other.b); } }; int main(void) { { A obj(1); obj = 2; //Passage par valeur: construction directe } { B obj(1); obj = 2; //Construction, puis référence, puis copie explicite } return 0; }