Non, il veut punir les programmeurs qui écrivent des choses idiotes comme :
Code : Sélectionner tout - Visualiser dans une fenêtre à part x = x;
Non, il veut punir les programmeurs qui écrivent des choses idiotes comme :
Code : Sélectionner tout - Visualiser dans une fenêtre à part x = x;
Loin de moi l'idée de punir qui que ce soit . En revanche, l'auto-affectation me semble être une erreur(d'inattention je suppose), donc pourquoi pas essayer de prévenir plutôt que gérer quelque chose qui n'a aucun sens?Non, il veut punir les programmeurs qui écrivent des choses idiotes comme :
Code : Sélectionner tout - Visualiser dans une fenêtre à part x = x;
- hp pavillon dv7
- intel(R) Core(TM)2 Duo CPU P8400 @ 2.26GHz 2.27GHz
- nVidia GeForce 9600M GT
- mémoire vive : 3.0Go
Sauf que lancer une exception pour ça (j'espère que c'est ce que fait ton Assert, si c'est un vrai assert c'est encore pire), alors qu'un comportement raisonnable et simple à implémenter est possible, c'est pas terrible...
Et puis comme toujours, autant surtout essayer de ne pas surprendre les utilisateurs avec des comportements qui sortent de la "normalité" : après tout i = i avec des types de base est parfaitement défini et valide, pourquoi ne pas suivre leur exemple ?
MAT.
Oui c'est bien une exception qui est lancé.j'espère que c'est ce que fait ton Assert, si c'est un vrai assert c'est encore pireJe suppose que tu as raison, c'est plus la "normalité" qui me choque alors mais bon ce que j'en pense.... (on s'en fiche)Et puis comme toujours, autant surtout essayer de ne pas surprendre les utilisateurs avec des comportements qui sortent de la "normalité" : après tout i = i avec des types de base est parfaitement défini et valide, pourquoi ne pas suivre leur exemple ?
- hp pavillon dv7
- intel(R) Core(TM)2 Duo CPU P8400 @ 2.26GHz 2.27GHz
- nVidia GeForce 9600M GT
- mémoire vive : 3.0Go
Oui, i = i c'est stupide, mais c'est légal et donc certains le feront, soit volontairement ou soit dans des cas plus subtile (comme mentionné plus haut, avec des pointeurs qui pointe la même chose par exemple)
C'est plutôt dans le sens ou je ne vois aucune utilité (mais dites le moi si je me trompe) à écrire x = x... Si ça n'a pas d'utilité, je me dit que c'est certainement une faute d'attention, comme l'a dit vdumont :Pourquoi dis-tu que ça n'a pas de sens?Des fois c'est subtile et involontaire
- hp pavillon dv7
- intel(R) Core(TM)2 Duo CPU P8400 @ 2.26GHz 2.27GHz
- nVidia GeForce 9600M GT
- mémoire vive : 3.0Go
En effet ajouter 0 n'a aucun intérêt mais ne provoquera pas d'erreur lors de ma surcharge, alors que dans mon cas je suis obligé d'ajouter un test juste pour gérer l'auto-affectation. C'est plus une question d'optimisation, maintenant si on me dit que je me fait des idées en pensant pouvoir gagner quelque chose, je suis prêt à admettre que mon raisonnement est ridiculeQuand tu surcharges l'addition, vérifies-tu si un des termes est nul (parce qu'ajouter 0 à un nombre n'a aucun intérêt, et peut être le fait d'une faute de frappe)?
- hp pavillon dv7
- intel(R) Core(TM)2 Duo CPU P8400 @ 2.26GHz 2.27GHz
- nVidia GeForce 9600M GT
- mémoire vive : 3.0Go
Justement, tu n'es pas obligé de faire le test d'auto-affectation, et en général c'est considéré comme un assez mauvais style de le faire. En effet, on préfère une approche qui évite les traitements de cas particuliers; dans le cas de l'affectation, on considère qu'il faut faire les modifications du membre gauche seulement après avoir fini de lire le membre droit. Ainsi, même si il a "aliasing" (si deux paramètres différents d'une fonction désignent un même objet), aucune modification du membre gauche n'affecte la valeur lue dans le membre droit. (Ada impose ceci en créant implicitement une copie temporaire du rhs et en invoquant après l'operator= surchargé - qui ne s'appelle pas "operator=", évidemment.)
Si on prend l'exemple de l'affectation d'une classe contenant un pointeur p nécessitant une copie profonde :
C'est un problème général : si une fonction a plusieurs paramètres pointeurs ou référence, que se passe t-il si elle est appelée en avec plusieurs référence liées au même objet/pointeurs pointant vers le même objet? (Les spécifications ne sont pas toujours suffisamment précises à ce sujet. )
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 C& C::operator= (const C& rhs) { // exemple très simple d'affectation : copie profonde du membre p // il se peut que rhs désigne aussi *this (rhs "aliase" *this) // mais on ne va faire des cas particuliers! // d'abord lire ce qu'il faut S_sub *dup = new S_sub (*rhs.p); // juste une copie profonde // à partir d'ici on ne lit plus la valeur de rhs // à partir d'ici on peut altérer l'objet *this // même si *this aliase rhs delete p; // peu importe si *p aliase *rhs.p, on a une copie! p = dup; }
Si l'aliasing est permis par une fonction, une solution générale pour l'implémenter est de copier les paramètres à lire avant d'effectuer les modifications. Cela couvre les cas d'alias entre paramètres d'entrée et paramètres de sortie.
En général, on s'attend à ce qu'operator= autorise l'aliasing. C'est non seulement le cas de l'affectation sur les types primitifs, c'est aussi exigé par les conteneurs standard : en l'état, ton smart-pointer ne peut pas être placé dans un std::vector, par exemple.
Je vais conclure par quelques (rappels de) recommandations générales, pour avoir l'air d'être un "sage" :
- utiliser les classes adéquates plutôt que d'avoir des pointeurs "nus" (string, vector) permet d'avoir les opérations de copie (constructeur de copie, opérateur d'affectation) sans rien faire de plus (si on avait utilisé un pointeur il aurai fallu programmer la copie profonde)
- utiliser un smart-pointer copropriétaire (par ex. shared_ptr) permet d'avoir une copie de pointeur avec gestion de la désallocation
- ne pas chercher à optimiser un cas particulier s'il n'est pas particulièrement fréquent
- ne pas réinventer la roue, sauf pour apprendre
Dans certains cas, il faut écrire les opérations de copie de ses classes. Dans la majorité des cas, l'assemblage de "briques" de base donne automatiquement la bonne sémantique. (C'est là la puissance et l'élégance du C++.)
Pourquoi il "ne peut pas" être placé dans un std::vector, c'est pourtant ce que j'ai fait et ça marche, dans quelles conditions mon vector ne fonctionnerait plus (mis à part l'auto-affectation bien sûr)?C'est non seulement le cas de l'affectation sur les types primitifs, c'est aussi exigé par les conteneurs standard : en l'état, ton smart-pointer ne peut pas être placé dans un std::vector, par exempleC'est vrai que là je suis entièrement d'accord, je vais m'empresser de corriger mes bétisesJustement, tu n'es pas obligé de faire le test d'auto-affectation, et en général c'est considéré comme un assez mauvais style de le faire. En effet, on préfère une approche qui évite les traitements de cas particuliers; dans le cas de l'affectation, on considère qu'il faut faire les modifications du membre gauche seulement après avoir fini de lire le membre droitÇa serait dommage de gâcher ça en effetC'est là la puissance et l'élégance du C++
Encore merci pour m'aider à y voir plus clair
- hp pavillon dv7
- intel(R) Core(TM)2 Duo CPU P8400 @ 2.26GHz 2.27GHz
- nVidia GeForce 9600M GT
- mémoire vive : 3.0Go
Parce que l'affectation n'est pas gérée correctement. Pour mettre un type dans un conteneur, il faut qu'il gère l'affectation correctement. C'est le contrat.
Un test ne démontre jamais qu'un programme est correct. (Dix, cent, mille tests non plus.) (Enfin, sauf dans le cadre de des systèmes déterministes hyper-simples qui peuvent être testés exhaustivement.)
N'importe quelle opération du vector qui a le droit d'utiliser l'opérateur d'affectation. En fait, n'importe quelle opération qui a le droit d'envoyer une exception, et qui a au moins un élément à copier (donc pas le constructeur par défaut, par exemple, ni le destructeur, ni clear(), mais à peu près tout le reste).
L'opérateur = répond donc aux critères? Pourtant je n'ai aucune exception lancée (ni "plantage") lorsque mon programme appel :N'importe quelle opération du vector qui a le droit d'utiliser l'opérateur d'affectation. En fait, n'importe quelle opération qui a le droit d'envoyer une exception, et qui a au moins un élément à copier (donc pas le constructeur par défaut, par exemple, ni le destructeur, ni clear(), mais à peu près tout le reste)La définition de m_Cameras :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 bool CSceneManager::AddCamera(CCameraPtr Camera) { //... m_Cameras.push_back( Camera ); m_Cameras[0] = Camera; // test return true; }
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2typedef CSmartPointer<CCamera> CCameraPtr; std::vector<CCameraPtr> m_Cameras;
- hp pavillon dv7
- intel(R) Core(TM)2 Duo CPU P8400 @ 2.26GHz 2.27GHz
- nVidia GeForce 9600M GT
- mémoire vive : 3.0Go
Ce n'est pas parce quelque chose fonctionne que c'est pour autant correct.
Ne pas respecter les pré-requis écrits dans la norme c'est risquer un comportement indéterminé.
Et un comportement indéterminé possible peut être de fonctionner correctement sur une certaine plate-forme avec une certaine version d'un certain compilateur.
MAT.
Oui mais à l'origine ma question était :Ce n'est pas parce quelque chose fonctionne que c'est pour autant correct.Et c'est pour cela que j'ai fait un test...dans quelles conditions mon vector ne fonctionnerait plus (mis à part l'auto-affectation bien sûr)?...Non je voulais tester si mon vector fonctionnait lorsque j'appelais la surcharge de l'opérator =.Ce code ne fait pas d'auto-affectation (si c'est ce que tu voulais tester ?) puisque l'ajout dans un vecteur se fait par copieOui en effet si mon code n'est pas portable sur d'autre compilateur on ne pas pas dire qu'il fonctionne (mais n'ayant qu'un compilateur je n'ai pas pu tester )Et un comportement indéterminé possible peut être de fonctionner correctement sur une certaine plate-forme avec une certaine version d'un certain compilateurJe n'ai rien contre la norme bien au contraire, ce qui me gênait au final c'était ce test utilisé seulement pour gérer l'auto-affectation, mais à partir du moment ou il y à des solutions plus "propre" (proposé par emmanuel deloget, medinoc et corrector), je prendsNe pas respecter les pré-requis écrits dans la norme c'est risquer un comportement indéterminé
- hp pavillon dv7
- intel(R) Core(TM)2 Duo CPU P8400 @ 2.26GHz 2.27GHz
- nVidia GeForce 9600M GT
- mémoire vive : 3.0Go
Eh bien, pour moi, la solution la plus propre pour l'affectation, c'est l'idiôme copy-swap.
Edit: Menues corrections.
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 template< class T > class CSmartPointer { public: CSmartPointer(void) : p(NULL), pr(NULL) { } explicit CSmartPointer(T *p) : p(p), pr(new int(1)) { } CSmartPointer(CSmartPointer const &src) : p(src.p), pr(src.pr) { if(pr != NULL) (*pr)++; } ~CSmartPointer() { if(pr != NULL) { (*pr)--; if(*pr == 0) { delete p; delete pr; } } } void Swap(CSmartPointer &other) { std::swap(p, other.p); std::swap(pr, other.pr); } CSmartPointer & operator= (CSmartPointer const &src) { CSmartPointer tmp(src); Swap(tmp); return *this; } private: T *p; int *pr; };
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
- Parce que j'ai oublié qu'on pouvait faire ça.
- Parce que la variable membre a le même nom que le paramètre, donc je ne sais pas si je peux faire ça
- C'est une bonne question. Je n'y ai pas réfléchi, en fait (je n'ai jamais programmé de pointeurs intelligents non-intrusifs, n'ayant joué qu'avec COM ou les CString de MFC...)
- Oups! Un oubli! J'ai retiré le const.
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
Oui. C'est justement le cas typique où on nomme un paramètre comme un membre, exprès : quand le membre est initialisé par le paramètre. C'est idiomatique, et probablement le seul cas où j'admettrai le masquage de noms de variables.
C'est très vraisemblablement ce voudra l'utilisateur, même si ce n'est techniquement pas commit-or-rollback, donc le constructeur lui-même n'est pas strong exception safe, mais il faut considérer le contexte d'utilisation :
Si CSmartPointer (T*) garanti la prise en charge même en cas d'échec ("fail-and-commit"), alors la déclaration de p est strong exception safe : soit l'objet créé est pris en charge par un CSmartPointer, soit tout est nettoyé.
Code : Sélectionner tout - Visualiser dans une fenêtre à part CSmartPointer<T> p (new T);
Tu es trop habitué à être const-correct!
(J'ai déjà fais exactement la même erreur. )
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager