Чтобы предотвратить копирование класса, вы можете легко объявить частные конструкторы-конструкторы копирования/назначения. Но вы также можете наследовать boost::noncopyable
.
В чем преимущества/недостатки использования boost в этом случае?
Чтобы предотвратить копирование класса, вы можете легко объявить частные конструкторы-конструкторы копирования/назначения. Но вы также можете наследовать boost::noncopyable
.
В чем преимущества/недостатки использования boost в этом случае?
Подводя итог тому, что говорили другие:
Преимущества boost::noncopyable
над частными методами копирования:
noncopyable
. Преимущества личных методов копирования поверх boost::noncopyable
:
Я не вижу преимуществ в документации:
#include <boost/noncopyable.hpp>
struct A
: private boost::noncopyable
{
};
против
struct A
{
A(const A&) = delete;
A& operator=(const A&) = delete;
};
Когда вы добавляете типы только для перемещения, я даже вижу документацию как вводящую в заблуждение. Следующие два примера не могут быть скопированы, хотя они являются подвижными:
#include <boost/noncopyable.hpp>
struct A
: private boost::noncopyable
{
A(A&&) = default;
A& operator=(A&&) = default;
};
против
struct A
{
A(A&&) = default;
A& operator=(A&&) = default;
};
При множественном наследовании может быть даже пространственный штраф:
#include <boost/noncopyable.hpp>
struct A
: private boost::noncopyable
{
};
struct B
: public A
{
B();
B(const B&);
B& operator=(const B&);
};
struct C
: public A
{
};
struct D
: public B,
public C,
private boost::noncopyable
{
};
#include <iostream>
int main()
{
std::cout << sizeof(D) << '\n';
}
Для меня это печатает:
3
Но это, что, я считаю, имеет превосходную документацию:
struct A
{
A(const A&) = delete;
A& operator=(const A&) = delete;
};
struct B
: public A
{
B();
B(const B&);
B& operator=(const B&);
};
struct C
: public A
{
C(const C&) = delete;
C& operator=(const C&) = delete;
};
struct D
: public B,
public C
{
D(const D&) = delete;
D& operator=(const D&) = delete;
};
#include <iostream>
int main()
{
std::cout << sizeof(D) << '\n';
}
Выходы:
2
Мне гораздо проще объявить мои операции копирования, чем рассуждать, если я получаю от boost::non_copyable
несколько раз, и если это будет стоить мне. Особенно, если я не являюсь автором полной иерархии наследования.
Это делает явным и ясным намерение, в противном случае нужно видеть определение класса и искать объявление, относящееся к семантике копирования, а затем искать спецификатор доступа, в котором он объявлен, для того чтобы определить, является ли класс не подлежащим копированию или нет. Другой способ обнаружить это, написав код, который требует использования семантики семантики, и увидеть ошибку компиляции.
Я не понимаю, почему никто больше не упоминает об этом, но:
С noncopyable
вы пишете имя своего класса только один раз.
Без, пятикратное дублирование: Один A для класса A, два для отключения назначения и два для отключения конструктора копирования.
Указание документации:
"Традиционный способ борьбы с ними состоит в том, чтобы объявить частный конструктор копирования и назначение копии, а затем документировать, почему это делается. Но получение неготовности является более простым и понятным и не требует дополнительной документации".
http://www.boost.org/libs/utility/utility.htm#Class_noncopyable
Одно конкретное преимущество (помимо выражения вашего намерения чуть более четко) заключается в том, что ошибка будет обнаружена раньше, на этапе компиляции не этап ссылки, если функция-член или друга пытается скопировать объект. Конструктор/назначение базового класса не доступны нигде, что дает ошибку компиляции.
Это также предотвращает случайное определение функций (например, ввод {}
вместо ;
)), небольшая ошибка, которая может остаться незамеченной, но которая позволит членам и друзьям сделать недопустимые копии объекта.
Преимущество состоит в том, что вам не нужно писать собственный конструктор копирования и частный оператор копирования самостоятельно, и он четко выражает ваше намерение, не написав дополнительную документацию.
A маленький недостаток (специфический GCC) заключается в том, что если вы скомпилируете свою программу с помощью g++ -Weffc++
, и у вас есть классы, содержащие указатели, например
class C : boost::noncopyable
{
public:
C() : p(nullptr) {}
private:
int *p;
};
GCC не понимает, что происходит:
предупреждение: "класс C" содержит элементы данных указателя [-WeffС++]
предупреждение: но не переопределяет 'C (const S &)' [-WeffС++]
предупреждение: или 'operator = (const C &)' [-WeffС++]
Пока он не будет жаловаться:
#define DISALLOW_COPY_AND_ASSIGN(Class) \
Class(const Class &) = delete; \
Class &operator=(const Class &) = delete
class C
{
public:
C() : p(nullptr) {}
DISALLOW_COPY_AND_ASSIGN(C);
private:
int *p;
};
PS Я знаю, что GCC -WeffС++ имеет несколько проблем. В любом случае код, который проверяет "проблемы", довольно упрощен, иногда помогает.
Отказ, по словам Скотта Мейерса, называется "не-натурным", если вам нужно найти его.
Я бы предпочел использовать boost:: noncopyable, чем вручную удалить или приватизировать конструктор копирования и оператор присваивания.
Однако я почти никогда не использую ни один из методов, потому что:
Если я создаю объект, не подлежащий копированию, должна быть причина, по которой он не копируется. Эта причина, в 99% случаев, состоит в том, что у меня есть члены, которые нельзя копировать осмысленно. Скорее всего, такие члены также будут лучше соответствовать частным реквизитам. Поэтому я делаю большинство таких классов следующим образом:
struct Whatever {
Whatever();
~Whatever();
private:
struct Detail;
std::unique_ptr<Detail> detail;
};
Итак, теперь у меня есть частная структура реализации, и поскольку я использовал std:: unique_ptr, мой класс верхнего уровня не копируется бесплатно. Ошибки ссылок, вытекающие из этого, понятны, потому что они говорят о том, как вы не можете скопировать std:: unique_ptr. Для меня это все преимущества boost:: noncopyable, а частная реализация - в один.
Преимущество этого шаблона позже, если я решит, что действительно хочу сделать мои объекты этого класса доступными для копирования, я могу просто добавить и реализовать конструктор и/или оператор присваивания без изменения иерархии классов.