web-dev-qa-db-fra.com

Comment rendre cet objet C++ non copiable?

Voir le titre.

J'ai:

class Foo {
   private:
     Foo();
   public:
     static Foo* create();
}

Que dois-je faire d'ici pour que Foo soit non copiable

Merci!

60
anon
class Foo {
   private:
     Foo();
     Foo( const Foo& ); // non construction-copyable
     Foo& operator=( const Foo& ); // non copyable
   public:
     static Foo* create();
}

Si vous utilisez boost, vous pouvez également hériter de noncopyable: http://www.boost.org/doc/libs/1_41_0/boost/noncopyable.hpp

EDIT: version C++ 11 si vous avez un compilateur prenant en charge cette fonctionnalité:

class Foo {
   private:
     Foo();
     Foo( const Foo& ) = delete; // non construction-copyable
     Foo& operator=( const Foo& ) = delete; // non copyable
   public:
     static Foo* create();
}
84
Klaim

Rendez le constructeur de la copie et l'opérateur d'affectation privés également. La déclaration suffit, vous n'avez pas à fournir d'implémentation.

25
Hans Passant

Un autre moyen d’interdire le constructeur de copie. Par commodité, une macro DISALLOW_COPY_AND_ASSIGN peut être utilisée:

// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
  TypeName(const TypeName&) = delete;      \
  void operator=(const TypeName&) = delete

Ensuite, en classe Foo:

class Foo {
 public:
  Foo(int f);
  ~Foo();

 private:
  DISALLOW_COPY_AND_ASSIGN(Foo);
};

ref de google style sheet

17
yesraaj
#include <boost/utility.hpp>
class Foo : boost::noncopyable {...

Mais comme l'a dit un jour Scott Meyers ... "C'est un bon cours, c'est juste que je trouve le nom un peu différent, err non naturel", ou quelque chose du genre.

16
Chris H

Pour ajouter un peu là.

Comme on l’a dit, la solution traditionnelle consiste à déclarer les deux Copy Constructor et Assignment Operator sous la forme private, et pas à définir eux.

  • Comme ils sont private, cela entraînera une erreur compile-time de la part de ceux qui essaieront de les utiliser et qui n’ont pas accès aux parties privées de la classe ...
  • Ce qui laisse les amis (et la classe elle-même) pour lesquels l'erreur se produira sous la forme de undefined symbol, soit à link-time (si vous les recherchez), soit très probablement à run-time ( en essayant de charger la bibliothèque).

Bien sûr, dans le second cas, le problème est considérable car vous devez alors vérifier votre code vous-même, car vous ne disposez pas de l'indication du fichier ni de la ligne sur laquelle l'erreur se produit. Heureusement, cela se limite à vos méthodes de cours et à vos amis.


De plus, il convient de noter que ces propriétés sont transitives sur la voie de l'héritage et de la composition: le compilateur ne générera que les versions par défaut du Default Constructor, du Copy Constructor, du Assignment Operator et du Destructor s'il le peut.

Cela signifie que pour l’un quelconque de ces quatre, ils sont automatiquement générés seulement s’ils sont accessibles pour toutes les bases et attributs de la classe.

// What does boost::noncopyable looks like >
class Uncopyable {
public:
  Uncopyable() {}

private:
  Uncopyable(const Uncopyable&);
  Uncopyable& operator=(const Uncopyable&);
};

C'est pourquoi hériter de cette classe (ou l'utiliser comme attribut) empêchera efficacement votre propre classe d'être copiée ou assignable à moins que vous ne définissiez vous-même ces opérateurs.

Généralement, l'héritage est choisi ici pour la composition pour deux raisons:

  • L'objet est effectivement Uncopyable, même si le polymorphisme peut ne pas être utile
  • L'héritage mène à EBO ou Empty Base Optimization, alors qu'un attribut sera adressable et occupera donc de la mémoire (dans chaque instance de la classe) même s'il n'en a pas réellement besoin, le compilateur a la possibilité de ne pas ajouter cette surcharge pour une classe de base.

Vous pouvez également déclarer les opérateurs privés et ne pas les définir dans votre propre classe, mais le code serait moins self-documenting et vous ne pourriez pas rechercher automatiquement les classes qui possèdent cette propriété ( sauf si vous avez un analyseur complet).

J'espère que cela a jeté un peu de lumière sur le mécanisme.

16
Matthieu M.

En C++ 11, vous pouvez explicitement désactiver la création du constructeur de copie et d'assignation par défaut en plaçant = delete après la déclaration.

De Wikipedia :

struct NonCopyable {
    NonCopyable() = default;
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable & operator=(const NonCopyable&) = delete;
};

La même chose vaut pour les cours bien sûr.

11
bobbaluba

La manière typique de rendre un objet C++ non copiable est de déclarer explicitement un constructeur de copie et un opérateur d'affectation de copie, sans toutefois les implémenter. Cela empêchera le compilateur de générer le sien. (En règle générale, cela est associé à leur déclaration private pour générer une erreur de compilation au lieu d'une erreur de l'éditeur de liens.)

Il existe également la classe boost::noncopyable dont vous pouvez hériter, ce qui correspond à ce que j'ai décrit ci-dessus.

4
jamesdlin

Rendre le constructeur de copie privé.

Foo(const Foo& src);

Vous n'avez pas besoin de l'implémenter, il suffit de le déclarer dans le fichier d'en-tête.

3
Roland Rabien

C'est ce que j'utilise:

/* Utility classes */

struct NoCopy
{
public:
    NoCopy() {}
private:
    NoCopy(const NoCopy &);
};

struct NoAssign
{
private:
    NoAssign &operator=(const NoAssign &);
};

struct NonInstantiable
{
private:
    NonInstantiable();
};

struct NoCopyAssign : NoCopy, NoAssign
{
};
typedef NoCopyAssign NoAssignCopy;

Dans ton cas:

struct Example : NoCopy
{
};
2
Andreas Bonini

La bonne pratique en C++ 11 est de déclarer le constructeur de copie et l'affectation supprimés publiquement . Non supprimé personnellement, publiquement supprimé: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines# Rc-delete

1
Matthieu Brucher