如何防止派生类重写一个方法?

20

如何强制基类方法不被派生类覆盖?


5
不要在基类中将其设为虚函数?那么它就无法被重写,只能被重载。如果问题是 Java 中的 final 方法是否具有直接等效物,则没有。 - Steve Jessop
如果没有使用虚拟关键字,我们就不能实现它,那么他们在JAVA中是如何实现的呢? - sriks
在Java中,将一个方法标记为"final"会禁止子类实现该方法,并且编译器和/或字节码验证程序将强制执行此操作。而在C++中,无法禁止子类实现该方法,只是根据"override"的定义,只有虚函数可以被"覆盖"。 - Steve Jessop
4
请注意,C++11 中增加了 final 限定符:https://dev59.com/cm855IYBdhLWcg3wNxkM#16896559 - moooeeeep
6个回答

38
如果您能够使用C++11中的final关键字,则可以防止派生类覆盖该方法。(微软编译器似乎支持具有相似语义的sealed。)
以下是一个示例:
#include <iostream>

struct base {
    // To derived class' developers: Thou shalt not override this method
    virtual void work() final {
        pre_work();
        do_work();
        post_work();
    }
    virtual void pre_work() {};
    virtual void do_work() = 0;
    virtual void post_work() {};
};

struct derived : public base {
    // this should trigger an error:
    void work() {
        std::cout << "doing derived work\n";
    }
    void do_work() {
        std::cout << "doing something really very important\n";
    }
};

int main() {
    derived d;
    d.work();
    base& b = d;
    b.work();
}

当我尝试编译它时,这是我得到的结果:

$ g++ test.cc -std=c++11
test.cc:17:14: error: virtual function ‘virtual void derived::work()’
test.cc:5:22: error: overriding final function ‘virtual void base::work()’

4
为什么在我不想让派生类重写它时,我应该将“work()”声明为“virtual”,而不是标记为“final”? - Sonic78
5
@Sonic78 很好的问题!在实际的代码中,你可能会有另一个基类作为base,它提供了一个虚函数,你希望防止派生自你的中间基类的类对其进行覆盖。 - moooeeeep

19

如果您将方法设置为非虚拟的,则派生类无法重写该方法。但是,在 C++03 中,一个类不能覆盖基类中的方法,并且还阻止进一步的派生类重写相同的方法。一旦方法被设置为虚拟的,它就会保持虚拟。


9
使用C++11中的<code>final</code>关键字可以避免派生类重写基类中的方法。 - tharinduwijewardane

3

不要将它设为虚拟的。

这并不能防止从您的类派生并隐藏函数(通过提供另一个具有相同名称的成员函数)。但是,如果您的类本来就不打算派生(没有虚析构函数,也没有虚成员函数),那么这不应该成为问题。


1

如果你想要保持它为公共的,那就别声明它为虚函数。

编辑:Srikanth 评论说想知道在派生类中重写一个私有成员函数的情况。

class A
{
public:
    virtual ~A(){};
    void test()
    {
        foo();
    };
private:
    virtual void foo()
    {
        std::cout << "A";   
    };
};


class B : public A
{
public:
    virtual void foo()
    {
        std::cout << "B";   
    };
};


void test()
{
    B b;
    A& a = b;

    a.test(); // this calls the derived B::foo()

    return 0;
}`

4
派生类仍然可以重写虚拟私有成员函数。 - Fred Larson
我已经编辑过了,谢谢纠正。 - Stephane Rolland
但是,我们如何在不使派生类成为友元或减少访问限定符的情况下覆盖私有方法? - sriks
@Srikanth,我已经编辑了答案并添加了示例代码。 - Stephane Rolland

0

派生类仍然可以覆盖虚拟私有成员函数。 - Fred Larson

-1

简短回答:没有必要。长话短说,你可以做一些小技巧,但这值得吗?


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