2010-01-31 1 views

ответ

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

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

EDIT: C++ 11 версию, если у вас есть компилятор, поддерживающий эту функцию:

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

Как интересно, почему вы сделали собственный конструктор по умолчанию закрытым и добавили метод create()? Какие преимущества имеет этот макет? – user3728501

+0

@EdwardBird Я просто использовал пример вопроса. Этот способ в основном похож на то, что он заставляет строить экземпляры конкретного типа через фабрику. Это полезно, если конструктор должен выполнить базовую настройку, а некоторые другие операции (возможно, разные в зависимости от контекста или платформы или что-то еще) должны быть выполнены до предоставления объекта или даже до создания объекта (возможно, некоторые манипуляции с пулом памяти). Я бы использовал тип unique_ptr или shared_ptr как create() лично. В любом случае основная причина заключалась в том, чтобы просто исправить вопрос. – Klaim

+0

А, спасибо, это хорошая вещь, о которой нужно знать. – user3728501

3

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

Foo(const Foo& src); 

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

25

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

+12

И вы не должны предоставлять реализацию. –

4

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

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

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

Но Скотт Мейерс однажды сказал ... «Это классный класс, просто я нахожу имя немного un, err non natural» или что-то в этом роде.

+0

Любая ссылка на контекст цитаты? – GManNickG

+0

он был либо в эффективном C++, либо более эффективном C++, книге. –

+3

Ссылка: Эффективный C++ (Третье издание) - Скотт Мейерс, пункт 6 – Thirler

16

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

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

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

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


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

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

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

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

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

В общем случае наследования выбирается над композицией там по 2 причинам:

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

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

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

+1

+1. Хороший ответ. – zeboidlund

+0

@chappjc: И вы думаете правильно! Oops ...: D –

+0

BTW, не является 'Uncopyable' неполным без явного определения конструктора, поскольку он не будет автоматически генерироваться из-за наличия других конструкторов? Например, вы получаете «никакого подходящего конструктора по умолчанию» с этим: http://rextester.com/SFWR22041 Спасибо за полезный ответ! Я особенно ценю мотивацию, которую вы дали для использования наследования. – chappjc

2

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

/* 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 
{ 
}; 
+2

Обратите внимание, что наследование таких классов полезности может отрицательно повлиять на размер класса, в зависимости от архитектуры ABI. Подробнее см. Http://trac.webkit.org/changeset/68414. Конечно, этот набор изменений упоминает Itanic и ничего другого - но стоит ли полагаться на отсутствие других архитектур, когда-либо сделавших это? Может быть. Это определенный риск, и объявление частного конструктора и оператора присваивания работает одинаково хорошо. –

17

Просто еще один способ запретить конструктор копирования, Для удобства 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); 
}; 

ref from google style sheet

+1

Ваше решение не работает с некоторыми компиляторами. Некоторые компиляторы C++ требуют, чтобы, если вы объявляете функцию члена класса, вы также должны определить ее, даже если она никогда не используется в коде. Поэтому вам нужно использовать {} с двумя объявлениями функций выше. – ThreeBit

+0

@ThreeBit. Если вы имеете в виду конструктор с одним параметром и деструктором, говоря «две функции», это декларация, и программист уже знает, что они будут иметь определение в другом месте. Помимо этого, это то же самое, что и принятый ответ. –

+0

@ThreeBit: Какие компиляторы вы имеете в виду? Если они это сделают, они не соответствуют стандарту. –

10

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

От Wikipedia:

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

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