En attendant unique_ptr<> (sic ), comment empêche l'assignation et la copie d'un objet ?
Suffit-il de déclarer le copy constructor et l'operator= en private ?
En attendant unique_ptr<> (sic ), comment empêche l'assignation et la copie d'un objet ?
Suffit-il de déclarer le copy constructor et l'operator= en private ?
Salut,
Une petite précision: il "suffit" de déclarer le constructeur par copie et l'opérateur d'assignation private, mais de ne surtout pas les définir
Autrement, tu placera certes une restriction à la copie et à l'assignation, mais tu ne l'interdira pas forcément
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
Pour forcer le linker à ne pas construire l'éxecutable ?
Plutôt pour forcer le linker à sortir sur une erreur si, par malheur, tu venais effectivement à invoquer un comportement de copie ou d'assignation
Car, si tu n'invoque jamais un comportement de copie ou d'assignation, bien que le compilateur ait estimé que ces comportements "existent", le linker ne se plaindra pas
Tout ce qui peut t'arrêter avant la période d'utilisation (AKA au moment de la compilation et / ou de l'édition de liens) est de nature à t'éviter des soucis parfois difficiles à résoudre en période d'utilisation
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
Si je passe par une nouvelle réponse, c'est parce que je prévois qu'elle va se transformer en roman...
Pour comprendre l'idée, considérons les choses depuis le début:
Si tu ne déclares pas l'un des "big fours" (constructeur, constructeur par copie, opérateur d'assignation et destructeur), le compilateur va les implémenter en accessibilité publique, ce qui les rend tout à fait accessibles de "n'importe où dans le code".
Si tu estimes qu'une classe ne peut pas être copiée ni / ou assignée, tu as tes raisons qui sont surement valables et qui n'ont pas à être remises en question.
Cependant, si tu déclares le constructeur par copie et / ou l'opérateur d'assignation en accessibilité privée et que tu les définis (que tu les implémente, si tu préfères), un code proche de
Le compilateur se plaindra parce que "le constructeur par copie est privé dans ce contexte"...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 void foo() { /* une instance "non copiée" */ MaClass mc: /* tu as oublié que MaClasse n'est pas sensée être copiable */ MaClass deux(mc); }
Tu as déjà avancé d'un grand pas
Mais si tu fais l'erreur dans une fonction membre de la classe, cela devient beaucoup plus dangereux...
Ainsi un code proche de
sera, non seulement accepté par le compilateur (après tout, MaClass(MaClass const&) est déclarée ) mais sera aussi acceptée par l'éditeur de lien (car le compilateur a bel et bien créé le code exécutable correspondant)...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 void MaClass::doSomething() { /* tu oublie que ta classe n'est pas sensée etre copiable */ MaClass temp(*this); /*...*/ }
Et, comme tu as surement des raisons pour avoir rendu ta classe non copiable, tu risque de te rendre compte du problème que... bien loin de l'invocation de doSomething...
La recherche de la raison pour laquelle ton instance de MaClass part en vrille deviendra une véritable galère... et occasionnera, au minimum un délais supplémentaire avant la remise du projet, au pire... le lancement d'une bombe atomique (allez, soyons sympa: un plantage lors de la démonstration de remise au client... ce qui fait quand même tache )
Par contre, si tu n'implémente pas le constructeur par copie et / ou l'opérateur d'affectation que tu as rendu privé(s), le linker te jettera lorsqu'il travaillera sur doSomething au motif qu'il rencontre uneC'est à dire, bien avant d'arriver chez le clientréférence indéfinie à <signes cabalistiques>MaClass(MaClass const&)<signes cabalistiques>
En plus, avec un peu de chance, il t'indiquera même dans quelle fonction tu dois aller chercher cet appel pour lequel il ne trouve aucun symbole
Tu sécurise donc à très peu de frais (et même en gagnant le temps de l'implémentation des deux méthodes) ta conception générale
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
Merci beaucoup pour la réponse exhaustive (bien qu'elle n'était pas nécessaire) on apprécie
A propos des "big fours" comme tu dis, j'en en un peu marre de les taper à tout bout de champ, alors que leur écriture est bien souvent récurrente et systématique: Copie/assignation des membres un à un, précédé éventuellement par un appel au parent quand il y a héritage. J'ai cru comprendre qu'il allait y avoir un nouveau mot clé "default" pour ne plus se taper cette... tape
Oui, il va y avoir un nouveau mot clé.
Par contre, je ne les tape jamais:
- soit mes membres sont implicitement copiables
- soit je manipule des entités, et donc dériver d'une classe noncopyable me suffit.
- soit je passe par un "wizard" de mon éditeur (qui génère des squelettes de casses à partir de 4 sémantiques types)
Pour unique_ptr, il sera implicitement déplaçable au point qu'il faille en plus dériver de noncopyable ?
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
A vrai dire, j'aime avoir la certitude que les gens auront compris la raison de mes conseils...
Ca permet d'assurer une certaine "fixation dans le temps" de ce qu'on leur a expliqué
N'oublie pas que, si le comportement par défaut te suffit, tu peux parfaitement décider de, tout simplement, ne pas les déclarer et laisser le soin au compilateur de se les "palucher" :
- Si ta classe est défaut constructible et n'utilise que des membres qui sont eux-même défaut constructible, tu peux "laisser tomber" la déclaration et l'implémentation du constructeur
- Si ta classe est copiable et qu'elle utilise des membres qui sont eux-même copiables, tu peux te passer de la déclaration et de l'implémentation du constructeur par copie
- Si ta classe est assignable et qu'elle utilise des membres qui sont eux-même assignables, tu peux te passer tu peux te passer de la déclaration et de l'implémentation de l'opérateur d'affectation
- Si, enfin, le destructeur ne fait rien d'autre que d'appeler le destructeur des membres de ta classes (hors allocation dynamique), tu peux te passer de la déclaration et de l'implémentation du destructeur
L'exception est le destructeur dans le cadre de l'héritage: même s'il ne fait rien de particulier, tu le déclarer (selon le cas) virtuel et public ou non virtuel et protégé, et donc l'implémenter
Pour le reste, il est malheureusement vrai que, si tu décide - par besoin - de redéfinir le comportement du constructeur par copie, de l'opérateur d'affectation ou du destructeur (pour une raison autre que le simple fait qu'il est virtuel), tu devras le plus souvent aussi envisager de redéfinir le deux autres
A méditer: La solution la plus simple est toujours la moins compliquée
Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
Compiler Gcc sous windows avec MinGW
Coder efficacement en C++ : dans les bacs le 17 février 2014
mon tout nouveau blog
Ha ! C'est une des raisons qui me poussent à coller des shared_ptr partout dès que possible (je suis feignant). Plus besoin d'écrire de constructeur de copie/opérateur d'assignement avec des shared_ptr membres vu que le comportement par défaut, la copie membre à membre, marche comme sur des roulettes.A propos des "big fours" comme tu dis, j'en en un peu marre de les taper à tout bout de champ, alors que leur écriture est bien souvent récurrente et systématique: Copie/assignation des membres un à un, précédé éventuellement par un appel au parent quand il y a héritage.
D'ailleurs à ce propos est ce que la régle du : on n'implémente le BIG four que dans le cas où il y'a des alloc dynamique (ie gestion de pointeurs) est vrai. et que donc dans le cas de classes ne gérant que des variables déclarés sur la pile les ctor et opérateur d'assignation fournies par le compilateur est suffisant, est une règle applicable en tout lieu?
Je me demande ça car j'épluche pas mal le code de boost en ce moment et je me rends compte que généralement il y'a un commentaire disant que le code généré par le compilo est suffisant.
"Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu
Tout dépend des sémantiques et des membres.
Cf l'article "Big Rule of Two" qui raffine la règle pour les classes valeur qui ont des membres non trivialement copiables.
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
Hum merci .
ps : l'article est bien celui d'artima?
"Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu
Oui. C'est bien celui là.
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
Il n'y aura pas de constructeur par déplacement ou d'opérateur égal par déplacement (j'ai du mal à lui trouver un nom que j'aime bien à celui-là) généré automatiquement par le compilateur. Donc avoir un membre de type unique_ptr rendra la classe non copiable par défaut, tout comme avoir un membre constant, ou un membre référence.
Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.
Je crois que je vais devoir revoir certains concepts de base que j'ai oublié (ou ignoré) à propos des "big fours". J'y reviendrai probablement dans un autre thread.
Pour revenir à la copie et l'assignation interdites, que se passe-t-il pour les classes héritées ? Par défaut, je suppose qu'elles héritent de la même "tare", càd ni copie ni assignation puisque l'accès aux méthodes correspondantes de la classe parente sera interdit.
Ouai, d'ailleurs il est pas rare de voir dans des projets une class nonCopyable déclaré juste comme ça et ainsi quand ils veulent rendre une class non copiable ils héritent de nonCopyable.
Il me semble _mais j'y mettrais pas ma main au feu_ que certains projets de boost sont fait comme ça.
"Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu
Oui et non :
boost::noncopiable est basé là dessus.
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 class A { public: A(){} private: A(const A&); A& operator=(const A&); }; class B :public A { public: B(){} B(const B&){} B& operator=(const B&){return *this;} }; class C :public A { public: C(){} }; int main() { B b; B b2(b);//OK b2 = b; // OK C c; C c2(c); // Erreur c2 = c; // Erreur return ; }
Ressources proposées par 3DArchi - Les fonctions virtuelles en C++ - Cours et tutoriels C++ - FAQ C++ - Forum C++.
Ben ouaip si tu les redéfinies dans la classe dérivée ça marche. J'aurais du préciser
"Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu
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