| auteur : 3DArchi |
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.
|
lien : Toute classe doit-elle être copiable ?
|
| auteur : 3DArchi |
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 :
class non_copyable
{
public :
non_copyable (){ }
private :
non_copyable (non_copyable const & );
non_copyable& operator = (non_copyable const & );
} ;
|
Les rendre private empêche de pouvoir les appeler à l'extérieur de la
classe. Le compilateur génère une erreur si on tente de les appeler.
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.
|
| auteur : 3DArchi |
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 :
class derived : private non_copyable
{
public :
derived (){ }
} ;
|
Cependant, la propriété peut se perdre si vous redéfinissez le
constructeur par copie ou l'opérateur d'affectation sans faire appel
à la méthode parente :
class derived : private non_copyable
{
public :
derived (){ }
derived (derived const & ){ }
derived& operator = (derived const & )
{ return * this ;}
} ;
derived a;
derived b (a);
derived c;
c = a;
|
Le constructeur non_copyable appelé dans derived
(derived const &) est ici le constructeur par défaut
non_copyable().
Boost propose une classe boost::noncopyable avec un
constructeur par copie et un opérateur d'affectation privés non définis :
# include <boost/noncopyable.hpp>
class my_class_non_copyable : private boost:: noncopyable
{
public :
my_class_non_copyable (){ }
} ;
|
L'héritage boost::noncopyable doit être privé. Une première raison
est que boost::noncopyable ne possède pas de destructeur virtuel public ! Ensuite,
l'utilisation polymorphe de boost::noncopyable n'a pas de sens.
Enfin, boost::noncopyable ne proposant pas de membres utilitaires
qui peuvent être appelés par les classes dérivées, un héritage
protected ne se justifie pas.
|
| auteurs : Luc Hermitte, 3DArchi |
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 :
# 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;
}
|
|
| auteur : Arzar |
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 :
class non_copyable
{
public :
non_copyable () = default ;
non_copyable (const non_copyable & ) = delete ;
non_copyable & operator = (const non_copyable & ) = delete ;
} ;
|
Cette approche possède quelques avantages par rapport à l'astuce utilisée en C++03 :
- 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.
|
| auteur : Arzar |
A l'heure ou nous rédigeons cette FAQ, une proposition pour ajouter un
utilitaire similaire à boost::noncopyable est encore en cours de
discussion dans le comité C++ ISO définissant le nouveau standard.
Si cette proposition est avalisée, il sera alors possible d'obtenir
une classe non copiable en dérivant depuis std::noncopyable :
# include <utility> // pour bénificier de std::noncopyable
class my_class_non_copyable : private std:: noncopyable
{
} ;
|
Si cette proposition n'est pas votée, voici comment recréer cet
utilitaire dans votre code :
struct noncopyable
{
noncopyable () = default ;
noncopyable (const noncopyable& ) = delete ;
noncopyable& operator = (const noncopyable& ) = delete ;
} ;
|
Comme en C++03, il faut toutefois noter que la non copiabilité peut
se perdre si vous redéfinissez le constructeur par copie ou l'opérateur
d'affectation dans la classe dérivée sans faire appel à la méthode
parent.
|
Consultez les autres F.A.Q.
|
|
Les 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 © 2008 Developpez LLC.
Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne
peut être faite de ce site ni 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.