Как сделать этот объект C ++ не подлежащим копированию? - PullRequest
64 голосов
/ 01 февраля 2010

См. Заголовок.

У меня есть:

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

Что мне нужно сделать, чтобы Foo не копировался?

Спасибо!

Ответы [ 10 ]

88 голосов
/ 01 февраля 2010
class Foo {
   private:
     Foo();
     Foo( const Foo& ); // non construction-copyable
     Foo& operator=( const Foo& ); // non copyable
   public:
     static Foo* create();
}

Если вы используете повышение, вы также можете наследовать от некопируемого: http://www.boost.org/doc/libs/1_41_0/boost/noncopyable.hpp

РЕДАКТИРОВАТЬ: C ++ 11 версия, если у вас есть компилятор, поддерживающий эту функцию:

class Foo {
   private:
     Foo();
     Foo( const Foo& ) = delete; // non construction-copyable
     Foo& operator=( const Foo& ) = delete; // non copyable
   public:
     static Foo* create();
}
25 голосов
/ 01 февраля 2010

Сделайте конструктор копирования и оператор присваивания частным. Достаточно просто декларации, вам не нужно предоставлять реализацию.

17 голосов
/ 01 февраля 2010

Просто еще один способ запретить конструктор копирования. Для удобства можно использовать макрос DISALLOW_COPY_AND_ASSIGN:

// 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

Затем в классе Foo:

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

 private:
  DISALLOW_COPY_AND_ASSIGN(Foo);
};

ссылка из таблицы стилей Google

17 голосов
/ 01 февраля 2010
#include <boost/utility.hpp>
class Foo : boost::noncopyable {...

Но, как однажды сказал Скотт Мейерс ... "Это хороший класс, просто я нахожу это имя немного не совсем натуральным" или что-то в этом роде.

16 голосов
/ 01 февраля 2010

Чтобы добавить туда немного.

Традиционное решение, как уже было сказано, объявляет и Copy Constructor и Assignment Operator как private, а не до определяет им.

  • Поскольку они private, это приведет к ошибке времени компиляции от любого, кто пытается их использовать, но не имеет доступа к закрытым частям класса ...
  • Из-за этого у друзей (и самого класса) ошибка будет возникать в виде undefined symbol, либо в время соединения (если вы их там найдете), либо, скорее всего, в время выполнения (при попытке загрузить библиотеку).

Конечно, во втором случае это довольно утомительно, потому что вам придется самостоятельно проверять свой код, поскольку у вас нет указания файла и строки, в которой возникает ошибка. К счастью, это ограничено вашими методами класса и друзьями.


Кроме того, стоит отметить, что эти свойства являются транзитивными по пути наследования и компоновки: компилятор будет генерировать только версии по умолчанию Default Constructor, Copy Constructor, Assignment Operator и Destructor, если он мая.

Это означает, что для любого из этих четырех они автоматически генерируются только , если они доступны для всех баз и атрибутов класса.

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

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

Вот почему наследование от этого класса (или использование его в качестве атрибута) будет эффективно препятствовать копированию или назначению вашего собственного класса, если вы сами не определите эти операторы.

Обычно наследование выбирается из состава по двум причинам:

  • Объект эффективно Uncopyable, даже если полиморфизм может быть не таким полезным
  • Наследование приводит к EBO или Empty Base Optimization, в то время как атрибут будет адресуемым и, следовательно, будет занимать память (в каждом экземпляре класса), даже если он на самом деле не нужен, у компилятора есть возможность не добавлять это накладные расходы для базового класса.

Вы также можете объявить операторы частными и не определять их в своем собственном классе, но код будет меньше самодокументирующимся , и вы не сможете автоматически искать те классы, которые тогда имейте это свойство (если у вас нет полноценного парсера).

Надеюсь, это пролило некоторый свет на механизм.

11 голосов
/ 29 ноября 2012

В C ++ 11 вы можете явно отключить создание конструктора копирования и присваивания по умолчанию, поместив = delete после объявления.

Из Википедия :

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

То же самое относится и к классам, конечно.

4 голосов
/ 01 февраля 2010

Типичный способ сделать объект C ++ не подлежащим копированию - это явно объявить конструктор копирования и оператор назначения копирования, но не реализовать их. Это предотвратит генерацию компилятором своего собственного. (Обычно это делается вместе с объявлением их private, чтобы вместо ошибки компоновщика генерировалась ошибка компиляции.)

Существует также класс boost::noncopyable, от которого вы можете наследовать, который выполняет то, что я описал выше.

3 голосов
/ 01 февраля 2010

Сделать конструктор копирования личным.

Foo(const Foo& src);

Вам не нужно это реализовывать, просто объявите это в заголовочном файле.

2 голосов
/ 01 февраля 2010

Вот что я использую:

/* 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;

В вашем случае:

struct Example : NoCopy
{
};
1 голос
/ 21 августа 2018

Хорошей практикой в ​​C ++ 11 является объявление конструктора копирования и назначения как публично удаленных.Лично не удалено, публично удалено: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-delete

...