在C ++ 11中禁用复制类的最简洁方法

时间:2014-05-17 09:24:37

标签: c++ c++11 destructor

我遇到了一个问题,因为当有一个用户定义的析构函数时,C ++ 11默认生成复制构造函数和复制赋值运算符,因此处于弃用状态。

对于大多数足够简单的类,默认生成的构造函数,运算符和析构函数都可以。请考虑以下原因来声明析构函数:

  1. 在基类中创建虚拟析构函数:

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

    在这些情况下,编译器是否会生成4个复制和移动特殊方法?如果是,那么我认为没关系,Base1Base2没有问题。

  2. 在析构函数中打印调试消息:

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

    我相信在这种情况下会生成复制构造函数和赋值运算符;但移动构造函数和赋值运算符不会。我想避免使用已弃用的默认生成并禁用D的复制。我还想避免使用4 D声明来充斥deleted。是否仅禁用一个复制构造函数?这是一种好风格吗?

4 个答案:

答案 0 :(得分:15)

使用C ++ 11,一种干净的方法是遵循boost中使用的模式(参见here

您基本上创建了一个基类,其中复制构造函数和复制赋值被删除,并继承它:

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
{
...
}

答案 1 :(得分:12)

删除复制构造函数和复制赋值运算符是禁用复制的最简单,最清晰的方法:

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;

答案 2 :(得分:7)

  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. 如果用户定义析构函数,仍会生成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,因为它们已经实现,广为人知且可识别。

  3. http://accu.org/index.php/journals/1896 - 这些问题的详细解释和理由。

答案 3 :(得分:1)

您可以通过此操作(Caffe: a fast open framework for deep learning使用该方法):

// Disable the copy and assignment operator for a class.
#define DISABLE_COPY_AND_ASSIGN(classname) \
private:\
  classname(const classname&);\
  classname& operator=(const classname&)

用法示例:

class CNoCopyable{

    public:
        CNoCopyable(int i):m_d(i){}

    private:
        int m_d;
        // add this line(pass class name)
        DISABLE_COPY_AND_ASSIGN(CNoCopyable);

};