IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
logo
Sommaire > Les classes en C++ > Sémantique de copie
        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 ?
        Quel est l'équivalent de boost::noncopyable en C++0x ?



Qu'est-ce qu'une classe copiable ?
Créé le 15/10/2009[haut]
auteur : 3DArchi
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.
copyable a;
copyable b = a; // ou copyable b(a);
Sémantiquement, il faut néanmoins prendre garde à ce que la copie d'un objet n'entraîne pas des effets indésirables :
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 !
}
lien : faq Je n'ai pas de constructeur par copie ni d'opérateur d'affectation (operator=) : ma classe est-elle copiable ?
lien : faq Toute classe doit-elle être copiable ?
lien : faq Comment rendre une classe non copiable ?
lien : faq Que se passe-t-il pour les classes dérivées d'une classe non copiable ?
lien : faq Quelle solution préférer (héritage ou redéfinition) pour rendre une classe non copiable ?
lien : faq Comment rendre une classe non copiable en C++0x ?
lien : faq Quel est l'équivalent de boost::noncopyable en C++0x ?

Je n'ai pas de constructeur par copie ni d'opérateur d'affectation (operator=) : ma classe est-elle copiable ?
Créé le 15/10/2009[haut]
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 : faq Toute classe doit-elle être copiable ?

Toute classe doit-elle être copiable ?
Créé le 15/10/2009[haut]
auteur : 3DArchi
Non. Par exemple, une classe à faq sémantique d'entité ne doit pas être recopiée. De même certaines enveloppes RAII (faq Resource Acquisition Is Initialization) peuvent avoir une politique interdisant la copie (par exemple un ScopedMutex). Citons encore les singletons.


Comment rendre une classe non copiable ?
Créé le 15/10/2009[haut]
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 &);// pas de définition !
   non_copyable& operator=(non_copyable const &);// pas de définition !
};
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.


Que se passe-t-il pour les classes dérivées d'une classe non copiable ?
Créé le 15/10/2009[haut]
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); // OK
   derived c;
   c = a;// OK
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 faq 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.


Quelle solution préférer (héritage ou redéfinition) pour rendre une classe non copiable ?
Créé le 15/10/2009[haut]
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 (faq 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;
 
}

Comment rendre une classe non copiable en C++0x ?
Créé le 15/10/2009[haut]
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:
   // 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;
};
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.


Quel est l'équivalent de boost::noncopyable en C++0x ?
Créé le 15/10/2009[haut]
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.


Valid XHTML 1.0 TransitionalValid CSS!

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.