FAQ C++Consultez toutes les FAQ
Nombre d'auteurs : 34, nombre de questions : 368, dernière mise à jour : 14 novembre 2021 Ajouter une question
Cette FAQ a été réalisée à partir des questions fréquemment posées sur les forums de http://www.developpez.com et de l'expérience personnelle des auteurs.
Je tiens à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose sont correctes ; les auteurs font le maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous trouvez une erreur ou si vous souhaitez devenir rédacteur, lisez ceci.
Sur ce, nous vous souhaitons une bonne lecture.
- Qu'est-ce qu'une classe copiable ?
- Je n'ai pas de constructeur par copie ni d'opérateur d'affectation (operator=) : ma classe est-elle copiable ?
- Toute classe doit-elle être copiable ?
- Comment rendre une classe non copiable ?
- Que se passe-t-il pour les classes dérivées d'une classe non copiable ?
- Quelle solution préférer (héritage ou redéfinition) pour rendre une classe non copiable ?
- Comment rendre une classe non copiable en C++0x ?
Une classe copiable permet à ses instances d'être copiées à l'identique d'un contenant vers un autre. En C++, syntaxiquement cette propriété est vérifiée dès qu'une classe possède un constructeur par copie ou un opérateur d'affectation.
Code c++ : | Sélectionner tout |
1 2 | copyable a; copyable b = a; // ou copyable b(a); |
Code c++ : | Sélectionner tout |
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 copy_with_problems { public: copy_with_problems() : m_probleme(new Type()) {} copy_with_problems(copy_with_problems const & copie_) : m_probleme(copie_.m_probleme) {} copy_with_problems& operator=(copy_with_problems const & rhs_) { delete m_probleme; m_probleme = copie_.m_probleme; return *this; } ~copy_with_problems() { delete m_probleme; } private: Type *m_probleme; }; int main() { copy_with_problems my_object; { copy_with_problems a_copy(my_object); } // au sortir on détruit le pointeur de a_copy qui est le même que celui // de my_object // ici my_object.m_probleme pointe sur une zone invalide ! } |
Toute classe doit-elle être copiable ?
Comment rendre une classe non copiable ?
Que se passe-t-il pour les classes dérivées d'une classe non copiable ?
Quelle solution préférer (héritage ou redéfinition) pour rendre une classe non copiable ?
Comment rendre une classe non copiable en C++0x ?
Quel est l'équivalent de boost::noncopyable en C++0x ?
Le compilateur génère des versions par défaut du constructeur par copie et de l'opérateur=.
Le constructeur par copie généré par le compilateur correspond à un appel du constructeur par copie des classes parentes et du constructeur par copie des membres.
Le comportement est le même pour l'opérateur = par défaut.
Notons toutefois que l'opérateur d'affectation ne peut être généré par le compilateur si la classe possède un membre constant ou un membre référence. Le constructeur par copie implicite est bien généré mais pas l'opérateur =.
De façon analogue, si la classe possède un membre non copiable alors ni le constructeur par copie ni l'opérateur d'affectation implicites ne peuvent être générés par le compilateur.
Peut-on autant dire que notre classe est copiable ? Non. Certes syntaxiquement un objet de cette classe peut être copié. Mais il faut encore vérifier si sémantiquement cela a une cohérence et ne risque pas de poser de problème. Une syntaxe correcte vous garantit que votre code compile, une sémantique bien définie assure une exécution maîtrisée.
Non. Par exemple, une classe à sémantique d'entité ne doit pas être recopiée. De même certaines enveloppes RAII ( Resource Acquisition Is Initialization ) peuvent avoir une politique interdisant la copie (par exemple un ScopedMutex). Citons encore les singletons.
Pour rendre une classe non copiable, il faut déclarer son constructeur par copie et son opérateur d'affectation en privé sans les définir :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | class non_copyable { public: non_copyable(){} //... private: non_copyable(non_copyable const &);// pas de définition ! non_copyable& operator=(non_copyable const &);// pas de définition ! }; |
Ne pas les définir empêche de les appeler à l'intérieur de la classe. L'erreur apparaît alors à l'édition de lien.
Si la classe dérivée ne redéfinit pas son constructeur par copie et/ou son opérateur d'affectation, la propriété non copiable est conservée :
Code c++ : | Sélectionner tout |
1 2 3 4 5 | class derived : private non_copyable { public: derived(){} }; |
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | class derived : private non_copyable { public: derived(){} derived(derived const &){} derived& operator=(derived const &) { return *this; } }; derived a; derived b(a); // OK derived c; c = a;// OK |
Boost propose une classe boost::noncopyable avec un constructeur par copie et un opérateur d'affectation privés non définis :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 | #include <boost/noncopyable.hpp> class my_class_non_copyable : private boost::noncopyable { public: my_class_non_copyable(){} }; |
Nous avons donc deux solutions pour rendre une classe non copiable : déclarer son constructeur par copie et son opérateur d'affectation sans les définir ou hériter d'une classe non copiable (par exemple, boost::noncopyable). La seconde approche est préférable, en particulier car les compilateurs peuvent avoir des comportements différents sur des aspects particuliers. Par exemple, le SFINAE ( Substitution Failure Is Not An Error) dans le code suivant ne se comporte pas de façon identique sur visual et gcc :
Code c++ : | Sélectionner tout |
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 | #include <iostream> template <typename T1, typename T2> struct CanConvert { typedef char True; class False { char dummy[2]; }; static True canConvert( T2 ); static False canConvert( ... ); static T1 makeT1( ); enum { r = sizeof(canConvert( makeT1( ) )) == sizeof(True) }; }; struct non_copyable { non_copyable() {} private: non_copyable(non_copyable const&); non_copyable& operator=(non_copyable const&); }; struct S1 { S1(int) {} private: S1(S1 const& ); S1& operator=(S1 const&); }; struct S2 : private non_copyable { S2(int) {} private: }; int main (int argc, char **argv) { std::cout << CanConvert<char, S1>::r << std::endl; std::cout << CanConvert<int*, S1>::r << std::endl; std::cout << CanConvert<char, S2>::r << std::endl; std::cout << CanConvert<int*, S2>::r << std::endl; } |
Le C++0x introduit un nouveau mécanisme facilitant l'écriture de classe non copiable : la suppression de fonction grâce à la syntaxe « =delete ».
Cette syntaxe permet de déclarer une fonction tout en supprimant sa définition. Elle peut s'appliquer aux fonctions membres spéciales (constructeurs, constructeur par copie, destructeur, opérateurs), ainsi qu'aux fonctions membres et aux fonctions libres - bref, à toutes les fonctions.
Pour rendre une classe non copiable, il faut supprimer son constructeur par copie et son opérateur d'affectation :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | class non_copyable { public: // force le compilateur à générer la version par défaut du constructeur. non_copyable() = default; // suppression du constructeur par copie et de l'opérateur d'affectation non_copyable(const non_copyable &) = delete; non_copyable & operator= (const non_copyable &) = delete; }; |
- La syntaxe pour supprimer une fonction est claire et non-ambigüe.
- L'appel à une fonction supprimée échoue à la compilation, jamais à l'édition des liens.
- Le message d'erreur est plus explicite et pointe directement sur la fonction supprimée.
Notez également l'utilisation de la syntaxe « =defaut » pour récupérer un constructeur par défaut généré automatiquement par le compilateur.
Proposer une nouvelle réponse sur la FAQ
Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour çaLes sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2024 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.