Наиболее краткий способ отключения класса копирования в С++ 11

У меня проблема с устаревшей версией с момента создания С++ 11 конструктора копирования и оператора присваивания копии, когда есть определяемый пользователем деструктор.

Для большинства достаточно простых классов конструкторы, операторы и деструкторы, созданные по умолчанию, являются точными. Рассмотрим следующие причины для объявления деструктора:

  • Создание тривиального деструктора виртуального в базовом классе:

    // header
    class Base1 { public: virtual ~Base1() = default; };
    class Base2 { public: virtual ~Base2(); };
    // source
    Base2::~Base2() = default;
    

    Все ли 4 копии и перемещение специальных методов будут сгенерированы компилятором в этих случаях? Если да, то я думаю, что это нормально, и нет необходимости усложнять Base1 или Base2.

  • Печать отладочного сообщения в деструкторе:

    // header
    class D { public: ~D(); };
    // source
    D::~D() {
    #ifdef DEBUG_THIS
        std::cout << "D was destructed." << std::endl;
    #endif
    }
    

    Я считаю, что в этом случае будет создан экземпляр-конструктор и оператор присваивания; но переместить конструктор и оператор присваивания не будет. Я хочу избежать использования устаревшего генерирования по умолчанию и отключения копирования D. Я также хочу избежать наводнения D объявлениями 4 deleted. Достаточно ли отключить только один конструктор экземпляра? Это хороший стиль?

Ответ 1

  • Оператор копирования и оператор присваивания копий будут сгенерированы, если деструктор явно установлен по умолчанию. И даже тогда их поколение устарело. Итак, чтобы иметь виртуальный деструктор и все методы по умолчанию, нужно написать следующее:

    struct Base
    {
        Base()=default;
        virtual ~Base() = default;
        Base(const Base&)=default;
        Base& operator=(const Base&)=default;
        Base(Base&&)=default;
        Base& operator=(Base&&)=default;
    };
    

    Я бы определенно использовал макрос для более чем одного такого класса Base.

  • Если деструктор определен пользователем, все еще генерируются 2 специальных метода. Существуют следующие способы отключить устаревший генератор генерирующих копий и оператор присваивания копии:

    • удалить конструктор перемещения ИЛИ переместить оператор присваивания (не совсем понятный, но очень короткий):

      Base(Base&&)=delete; // shorter than deleting assignment operator
      
    • удалить оба конструктора копирования и оператора присваивания копии:

      Base(const Base&)=delete;
      Base& operator=(const Base&)=delete;
      

    Обратите внимание, что вам нужно явно объявить конструктор по умолчанию, если вам это нужно, например. Base()=default;.

    Для этой цели также может использоваться макрос или наследование специального класса, но я лично предпочитаю удалять конструктор перемещения для реализации моего собственного макроса или базового класса. При использовании Qt или boost я бы предпочел Q_DISABLE_COPY(Base) и наследовать boost::noncopyable соответственно, потому что они уже реализованы, широко известны и узнаваемы.

http://accu.org/index.php/journals/1896 - подробное объяснение и обоснование этих проблем.

Ответ 2

С С++ 11 чистый способ - следовать шаблону, используемому в boost (см. здесь)

В основном вы создаете базовый класс, в котором конструктор копирования и назначение копии удаляются и наследуют его:

class non_copyable
{
protected:
    non_copyable() = default;
    ~non_copyable() = default;

    non_copyable(non_copyable const &) = delete;
    void operator=(non_copyable const &x) = delete;
};

class MyClass: public non_copyable
{
...
}

Ответ 3

Удаление оператора-копиратора и оператора присваивания копий является самым простым и ясным способом отключения копирования:

class X
{
    X(X const &) = delete;
    void operator=(X const &x) = delete;
};

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

Если список удаленных функций вас беспокоит, вы можете скрыть их за макросом, я думаю.

 #define NON_COPYABLE_NOR_MOVABLE(T) \ 
      T(T const &) = delete; \
      void operator=(T const &t) = delete; \
      T(T &&) = delete;