从派生类中删除虚函数

Delete virtual function from a derived class

本文关键字:函数 删除 派生      更新时间:2023-10-16

我有一个不应该在特定派生类中使用的虚拟基类函数。有办法"删除"它吗?我当然可以给它一个空定义,但我宁愿让它的尝试使用抛出一个编译时错误。c++ 11 delete说明符似乎是我想要的,但是

class B
{
    virtual void f();
};
class D : public B
{
    virtual void f() = delete; //Error
};

不会编译;至少,Gcc明确地不允许我删除具有未删除基本版本的函数。是否有其他方法可以获得相同的功能?

这是标准不允许的,但是您可以使用以下两种方法之一来获得类似的行为:

第一种方法是使用using将方法的可见性更改为private,从而阻止其他人使用它。这种解决方案的问题是,在超类的指针上调用方法不会导致编译错误。
class B
{
public:
    virtual void f();
};
class D : public B
{
private:
    using B::f;
};

到目前为止,我发现在调用D的方法时获得编译时错误的最佳解决方案是使用具有从false_type继承的通用结构的static_assert。只要没有人调用该方法,结构体就保持不变,static_assert就不会失败。

然而,如果调用方法,则定义了该结构体并且其值为false,因此static_assert失败。

如果方法没有被调用,但是你试图在超类的指针上调用它,那么D的方法没有定义,你会得到undefined reference编译错误。

template <typename T>
struct fail : std::false_type 
{
};
class B
{
public:
    virtual void f() 
    {
    }
};
class D : public B
{
public:
    template<typename T = bool>
    void
    f()
    {
        static_assert (fail<T>::value, "Do not use!");
    }
};
另一种解决方法是在使用方法时抛出异常,但这只会在运行时抛出。

标准不允许在派生类中删除基类的任何成员:
这样做会破坏继承,特别是"is-a";关系。

由于相关的原因,它不允许派生类定义在基类中删除的函数:
钩子不再是基类契约的一部分,因此它阻止你依赖于先前不再有效的保证。


如果你想变得更复杂,你可以强制一个错误,但它必须是链接时的,而不是编译时的:
声明成员函数,但不要定义它(但这并不能100%保证对虚函数有效)。
最好还看看GCC deprecated属性,以获得早期警告__attribute__ ((deprecated))
有关细节和类似的MS魔法:c++标记为已弃用

"我有一个虚基类函数,它永远不应该在特定的派生类中使用。"

在某些方面,这是一个矛盾。虚函数的全部意义在于提供基类提供的契约的不同实现。你要做的是打破合同。c++语言的设计就是为了防止您这样做。这就是为什么在实例化对象时强制实现纯虚函数的原因。这就是为什么它不允许你删除合同的一部分

所发生的是一件好事。它可能会阻止您实现不适当的设计选择。

然而

:

有时可以适当地使用不做任何事情的空白实现:

void MyClass::my_virtual_function()
{
    // nothing here
}

或者返回"failed"状态的空白实现:

bool MyClass::my_virtual_function()
{
    return false;
}

这完全取决于你想做什么。也许如果你能提供更多关于你想要达到的目标的信息,有人可以为你指出正确的方向。

编辑

仔细想想,为了避免为特定的派生类型调用函数,调用者需要知道它调用的是什么类型。调用基类引用/指针的全部意义在于,您不知道哪个派生类型将接受调用。

您可以做的就是在派生实现中抛出一个异常。例如,Java Collections框架在这方面做得非常过分:当对不可变的集合执行更新操作时,相应的方法只是抛出一个UnsupportedOperationException。在c++中也可以这样做。

当然,这只会在运行时显示函数的恶意使用;不是在编译时。然而,使用虚拟方法,由于多态,无论如何都无法在编译时捕获此类错误。例如:

B* b = new D();
b.f();

这里,您将D存储在B*变量中。所以,即使有一种方法告诉编译器你不允许在D上调用f,编译器也无法在这里报告这个错误,因为它只看到B

与其变得棘手,为什么不使用适当的继承:

class B { 
  // B is the generalization that provides common members
};
// Then you define appropriate specializations of B to derive from
class C1: public B {};
class C2: public B { 
  virtual void f() = 0;
};
class D: public C1 {
  // Inherits from B, and doesn't inherit void f()
};

我有一个虚基类函数,不应该在特定的派生类中使用

c++ 11提供了一个关键字final来防止虚函数被覆盖。

查看:http://en.cppreference.com/w/cpp/language/final .

class B
{
  virtual void f() final;
};
class D : public B
{
  // virtual void f();  // a compile-time error
  // void f() override; // a compile-time error
  void f(); // non-virtual function, it's ok
};