基类虚析构函数 - 五个规则?

3

我有一个基础的State接口类,带有虚拟默认析构函数。

class State {
public:
    virtual void event() = 0;

    virtual ~State() = default; // relevant part

    virtual void onCreate() {}
    virtual void onDestroy() {}
    virtual void onActivate() {}
    virtual void onDeactivate() {}
};

然后是一些继承自它的类:

class GameState : public State {
public:
    void event() override;
    // ...
};

class MenuState : public State {
public:
    void event() override;
    // ...
};

如果未定义复制操作或析构函数,编译器将生成默认的移动操作。 如果未定义移动操作,则编译器将生成默认的复制操作。

  1. 声明虚拟默认析构函数是否有效地删除了默认移动操作?

  2. 如果基类隐式删除了其移动操作,并且基类只是一个没有数据成员的接口,则派生类的移动操作是否有效?

  3. 在这里遵循“五个规则”真的有意义吗? 显式删除或默认所有5个特殊成员函数似乎相当臃肿。


顺便问一下,在基类State中的这些空定义方法有什么不妥之处吗?它们只是传递(状态机无论如何都会调用它们),派生类可以选择是否重写它们。 - undefined
1
对于这三个操作,都将返回“是”。由于“State”为空,因此复制和移动都是空操作,并不会产生任何负面影响。 - undefined
1
@sp2danny - 你说的“有效删除”是什么意思?它们并没有真正被删除 - 当使用右值参数调用时,通常会执行复制操作。 - undefined
1
它们没有被定义。这意味着重载解析将找到复制操作。 - undefined
1
可能明确默认复制构造函数和复制赋值运算符是个好主意,因为这些构造函数的隐式声明已被弃用:http://eel.is/c++draft/depr.impldec - undefined
在类层次结构中,通常最好不要进行任何类型的复制或移动操作,以避免意外的对象切割。 - undefined
1个回答

3
假设您的基类具有复制(可能还有移动)构造函数和赋值运算符。在这种情况下,您会期望发生什么?
MenuState menuState;
State state = menuState; // be aware that this State object is not a MenuState any more

如果你的State类是抽象的(例如,如果onActivate()是纯虚函数),上面的内容就没有任何意义,因为不允许创建State实例。
但即使不是抽象类,当你将MenuState成员放入State对象时,它们也会被切割。如果基类的实例本身没有太多意义,最好删除复制和移动构造函数以及赋值运算符。
对于派生(具体)类来说,复制或移动可能更有意义。在这种情况下,在基类中拥有protected的复制构造函数和赋值运算符可能会很有用。这样你就不能实例化基类本身,但派生类可以使用它。
MenuState menuState;
MenuState copy = menuState; // might be useful

如果基类本身是有意义的,那么公共(可能还有默认的)拷贝和移动构造函数以及赋值运算符是合理的。这种情况可能发生在派生类只是添加了一些额外信息的情况下,而这些信息在将派生对象转换为基类对象时并不需要。
所以这真的取决于具体情况,这就是为什么最好明确指出意图,是公共拷贝和移动、受保护的拷贝和移动,还是根本没有。
然而,这样的类层次结构通常与指针一起使用(例如std::unique_ptr或std::shared_ptr)。这时,虚拟的clone()方法有时更有用。
auto state = std::make_unique<MenuState>();
std::unique_ptr<State> copy = state->clone();

在内部,这个`clone()`方法可能使用拷贝构造函数,尽管这个构造函数可以是`public`(如果你的派生类不是`final`,要小心切片问题)或者`protected`(如果你想确保只有`clone()`方法被使用)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接