在基类中删除复制和移动构造函数/赋值运算符是否足够?

时间:2019-03-05 16:36:28

标签: c++ c++11 deleted-functions

如果我有一个抽象基类,并且想使所有派生类都不可复制和不可移动,那么声明在基类中删除的这些特殊成员函数就足够了吗?我想确保我的整个类层次结构是不可复制和不可移动的,并且想知道是否可以不必在每个派生类中将这4个特殊成员函数声明为已删除。我看到一个SO答案,似乎暗示着尽管派生类被从基类中删除了,但派生类仍可以显式声明副本或移动构造函数,但以下示例在尝试定义默认副本赋值运算符时导致编译错误,因此我不确定。这是错误:

  

derived_class.cc:15:15:错误:默认情况下,此副本构造函数将在第一次声明后将其删除   DerivedClass :: DerivedClass(const DerivedClass&)=默认值;

     

derived_class.h:9:22:注意:'DerivedClass'的副本构造函数被隐式删除,因为基类'virtual_functions :: BaseClass'具有已删除的副本构造函数   class DerivedClass:公共BaseClass {

     

base_class.h:11:3:注意:“ BaseClass”已在此处明确标记为已删除     BaseClass(const BaseClass&)= delete;

// base_class.h
class BaseClass {
public:
  BaseClass(const BaseClass &) = delete;
  BaseClass(BaseClass &&) = delete;
  BaseClass &operator=(const BaseClass &) = delete;
  BaseClass &operator=(BaseClass &&) = delete;
  virtual ~BaseClass() = default;
  virtual bool doSomething() = 0;

protected:
  BaseClass(std::string name);

private:
  std::string name_;
};

// derived_class.h
class DerivedClass : public BaseClass {
public:
  DerivedClass();
  DerivedClass(const DerivedClass &);
  bool doSomething() override;
};

// derived_class.cc
DerivedClass::DerivedClass(const DerivedClass &) = default;

3 个答案:

答案 0 :(得分:5)

Is deleting copy and move constructors/assignment operators in base class enough?

It is enough to prevent implicitly generated copy and move constructors/ assignment operators.

I saw a SO answer where it seemed to imply that a derived class could explicitly declare a copy or move constructor despite being deleted from the base class

This is correct. You cannot prevent this. Well, you can prevent this by declaring the class final. Then there cannot be derived classes, and thus derived classes cannot be copyable.

Of course, such explicitly declared copy constructor (and other) will not be able to copy the base sub object that is non-copyable. The constructors must use BaseClass(std::string) and the assignment operators cannot modify the state of the base object in any way (unless they use some trick to get around access specifier encapsulation).

答案 1 :(得分:4)

您不能阻止子类定义其自己的复制/移动构造函数。就是说,这将防止它“开箱即用”,这意味着,如果您不提供它,或者使用内联默认构造函数,它也将被标记为已删除。当您尝试仅将构造函数定义为默认值时,在此处出现错误的原因是,当成员或基础隐式删除构造函数时,不允许您在离线定义中执行此操作。你曾经用过

class DerivedClass : public BaseClass {
public:
  DerivedClass(const DerivedClass &) = default;
  bool doSomething() override;
};

然后将编译代码,并且只有在您实际尝试调用复制构造函数时才会出现错误。之所以可行,是因为即使成员或基础隐式删除了内联默认值,并且最终结果是构造函数也被隐式删除,也允许使用内联隐式默认值。

答案 2 :(得分:1)

您不能阻止派生类声明复制/移动构造函数,但不能将其默认设置:默认的复制ctor或派生类将尝试调用其基类的复制ctor(与move相同)。

但是派生类可以使用默认ctor显式构造其基数:

class DerivedClass : public BaseClass {
public:
  DerivedClass();
  DerivedClass(const DerivedClass &): BaseClass() {
      // copy ctor for the derived part
  }
  bool doSomething() override;
};

等等,... DerivedClass类现在可以复制了,尽管它的基类不是!