Каковы преимущества boost:: noncopyable

Чтобы предотвратить копирование класса, вы можете легко объявить частные конструкторы-конструкторы копирования/назначения. Но вы также можете наследовать boost::noncopyable.

В чем преимущества/недостатки использования boost в этом случае?

Ответ 1

Подводя итог тому, что говорили другие:

Преимущества boost::noncopyable над частными методами копирования:

  • Это более явное и описательное в намерении. Использование частных функций копирования - это идиома, которая занимает больше времени, чем noncopyable.
  • Это меньше кода/меньше набрав/меньше помех/меньше места для ошибки (самый простой случай будет непредсказуем для реализации).
  • Он вводит значения прямо в метаданные типа, аналогичные атрибуту С#. Теперь вы можете написать функцию, которая принимает только объекты, которые не подлежат копированию.
  • Он потенциально обнаруживает ошибки ранее в процессе сборки. Ошибка будет представлена ​​во время компиляции, а не времени ссылки, в том случае, если сам класс или друзья класса совершают ошибочное копирование.
  • (почти то же, что и # 4) Предотвращает вызов класса или друзей класса из методов частной копии.

Преимущества личных методов копирования поверх boost::noncopyable:

  • Нет зависимости от перенапряжения

Ответ 2

Я не вижу преимуществ в документации:

#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 несколько раз, и если это будет стоить мне. Особенно, если я не являюсь автором полной иерархии наследования.

Ответ 3

Это делает явным и ясным намерение, в противном случае нужно видеть определение класса и искать объявление, относящееся к семантике копирования, а затем искать спецификатор доступа, в котором он объявлен, для того чтобы определить, является ли класс не подлежащим копированию или нет. Другой способ обнаружить это, написав код, который требует использования семантики семантики, и увидеть ошибку компиляции.

Ответ 4

  • Цель boost:: noncopyable более ясна.
  • Boost:: noncopyable предотвращает случайное использование методов классов с помощью частного конструктора копирования.
  • Меньше кода с boost:: noncopyable.

Ответ 5

Я не понимаю, почему никто больше не упоминает об этом, но:

С noncopyable вы пишете имя своего класса только один раз.

Без, пятикратное дублирование: Один A для класса A, два для отключения назначения и два для отключения конструктора копирования.

Ответ 6

Указание документации:

"Традиционный способ борьбы с ними состоит в том, чтобы объявить частный конструктор копирования и назначение копии, а затем документировать, почему это делается. Но получение неготовности является более простым и понятным и не требует дополнительной документации".

http://www.boost.org/libs/utility/utility.htm#Class_noncopyable

Ответ 7

Одно конкретное преимущество (помимо выражения вашего намерения чуть более четко) заключается в том, что ошибка будет обнаружена раньше, на этапе компиляции не этап ссылки, если функция-член или друга пытается скопировать объект. Конструктор/назначение базового класса не доступны нигде, что дает ошибку компиляции.

Это также предотвращает случайное определение функций (например, ввод {} вместо ;)), небольшая ошибка, которая может остаться незамеченной, но которая позволит членам и друзьям сделать недопустимые копии объекта.

Ответ 8

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

Ответ 9

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С++ имеет несколько проблем. В любом случае код, который проверяет "проблемы", довольно упрощен, иногда помогает.

Ответ 10

Отказ, по словам Скотта Мейерса, называется "не-натурным", если вам нужно найти его.

Ответ 11

Я бы предпочел использовать boost:: noncopyable, чем вручную удалить или приватизировать конструктор копирования и оператор присваивания.

Однако я почти никогда не использую ни один из методов, потому что:

Если я создаю объект, не подлежащий копированию, должна быть причина, по которой он не копируется. Эта причина, в 99% случаев, состоит в том, что у меня есть члены, которые нельзя копировать осмысленно. Скорее всего, такие члены также будут лучше соответствовать частным реквизитам. Поэтому я делаю большинство таких классов следующим образом:

struct Whatever {
  Whatever();
  ~Whatever();
  private:
  struct Detail;
  std::unique_ptr<Detail> detail;
};

Итак, теперь у меня есть частная структура реализации, и поскольку я использовал std:: unique_ptr, мой класс верхнего уровня не копируется бесплатно. Ошибки ссылок, вытекающие из этого, понятны, потому что они говорят о том, как вы не можете скопировать std:: unique_ptr. Для меня это все преимущества boost:: noncopyable, а частная реализация - в один.

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