Каковы преимущества boost :: noncopyable - PullRequest
63 голосов
/ 19 октября 2011

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

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

Ответы [ 11 ]

46 голосов
/ 20 октября 2011

Я не вижу никакой выгоды от документации:

#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;
};

vs:

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

42 голосов
/ 19 октября 2011

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

41 голосов
/ 20 октября 2011

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

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

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

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

  1. Отсутствует зависимость от повышения
16 голосов
/ 30 октября 2011

Я не могу понять, почему никто другой, кажется, не упоминает об этом, но:

С noncopyable вы пишете название своего класса только один раз.пятикратное дублирование : один A для «класса A», два для отключения назначения и два для отключения конструктора копирования.

16 голосов
/ 19 октября 2011
  1. Цель boost :: noncopyable более ясна.
  2. Boost :: noncopyable предотвращает случайное использование методов классов при помощи конструктора частной копии.
  3. Меньше кода с boost :: noncopyable.
9 голосов
/ 19 октября 2011

Цитирование документации:

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

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

8 голосов
/ 19 октября 2011

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

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

3 голосов
/ 05 марта 2014

A small Недостаток (специфичный для GCC) заключается в том, что, если вы компилируете свою программу с g++ -Weffc++ и у вас есть классы, содержащие указатели, например,

class C : boost::noncopyable
{
public:
  C() : p(nullptr) {}

private:
  int *p;
};

GCC не понимаетчто происходит:

предупреждение: у 'класса C' есть члены с указателями данных [-Weffc ++]
предупреждение: но не переопределяет 'C (const S &)' [-Weffc ++]
предупреждение: или 'operator = (const C &)' [-Weffc ++]

Хотя он не будет жаловаться на:

#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 -Weffc ++ есть несколько проблем.Код, который проверяет «проблемы», довольно упрощен, во всяком случае ... иногда это помогает.

3 голосов
/ 19 октября 2011

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

2 голосов
/ 20 октября 2011

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

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

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

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

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

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

...