【问题标题】:Is there a way to prevent a method from being overridden in subclasses?有没有办法防止方法在子类中被覆盖?
【发布时间】:2010-09-06 06:12:51
【问题描述】:

是否有人知道 C++ 中的语言特性或技术可以防止子类覆盖父类中的特定方法?

class Base {
public:
    bool someGuaranteedResult() { return true; }
};

class Child : public Base {
public:
    bool someGuaranteedResult() { return false; /* Haha I broke things! */ }
};

即使它不是虚拟的,这仍然是允许的(至少在我使用的 Metrowerks 编译器中),你得到的只是一个关于隐藏非虚拟继承函数 X 的编译时警告。

【问题讨论】:

    标签: c++ overriding metrowerks


    【解决方案1】:

    当您可以将final 说明符用于虚拟方法(由C++11 引入)时,您可以做到。让我引用my favorite doc site

    在虚函数声明中使用时,final 指定该函数不能被派生类覆盖。

    适应你的例子:

    class Base {
    public:
        virtual bool someGuaranteedResult() final { return true; }
    };
    
    class Child : public Base {
    public:
        bool someGuaranteedResult() { return false; /* Haha I broke things! */ }
    };
    

    编译时:

    $ g++ test.cc -std=c++11
    test.cc:8:10: error: virtual function ‘virtual bool Child::someGuaranteedResult()’
    test.cc:3:18: error: overriding final function ‘virtual bool Base::someGuaranteedResult()’
    

    当您使用 Microsoft 编译器时,还请查看 sealed 关键字。

    【讨论】:

    • 我删除了 virtual 并且它显示了基本调用函数的错误 ... final 仅适用于虚拟函数?
    • @AbhimanyuAryan 正确 final 仅适用于虚拟方法,因此不适用于此问题
    • 请注意(至少在 Visual C++ 中)如果将 const 方法标记为 final,则需要 const final 而不是 final const。
    【解决方案2】:

    几个想法:

    1. 将您的函数设为私有。
    2. 不要将您的函数设为虚拟函数。不过,这实际上并不能防止函数被另一个定义遮蔽。

    除此之外,我不知道有一种语言功能会以这样的方式锁定您的函数,从而防止它被重载并且仍然能够通过对子类的指针/引用来调用。

    祝你好运!

    【讨论】:

    • 但是我们不能从其他类调用该方法。 ?
    • 请注意,在 C++11 中添加了一个 final 说明符:stackoverflow.com/a/16906116/1025391
    • @moooeeeep final 在这种情况下不起作用:struct foo { void bar() final; }; is not legal C++
    • @Jean-MichaëlCelerier 是的,您也需要将其设为虚拟才能正常工作。
    • @Jean-MichaëlCelerier 我明白了,但有时你可以做到。这就是我添加注释的原因。
    【解决方案3】:

    听起来您正在寻找的就是 prevents a method from being overridden by a subclass 的 Java 语言 final 关键字。

    因为others 这里有suggested,你真的无法阻止。另外,这似乎是一个相当frequently asked question

    【讨论】:

    【解决方案4】:

    (a) 我不认为将函数设为私有是解决方案,因为那样只会对派生类隐藏基类函数。派生类总是可以定义具有相同签名的新函数。 (b) 使函数非虚拟也不是一个完整的解决方案,因为如果派生类重新定义了相同的函数,则始终可以通过编译时绑定来调用派生类函数,即 obj.someFunction() 其中 obj 是派生类。

    我认为没有办法做到这一点。另外,我想知道您决定禁止派生类覆盖基类函数的原因。

    【讨论】:

      【解决方案5】:

      关于隐藏非虚拟继承函数 X 的编译时警告。

      更改您的编译器设置,使其成为错误而不是警告。

      【讨论】:

        【解决方案6】:

        我猜编译器警告你的东西是隐藏的!它实际上被覆盖了吗?

        编译器可能会给你一个警告,但是在运行时,如果指针的类型是父类,则无论它指向的对象的实际类型如何,都会调用父类方法。

        这很有趣。尝试为您的编译器制作一个小的独立测试程序。

        【讨论】:

          【解决方案7】:

          为了澄清,你们中的大多数人误解了他的问题。他不是在问“覆盖”一种方法,而是在问是否有办法防止“隐藏”。简单的答案是“没有!”。

          这又是他的例子

          父类定义一个函数:

          int foo() { return 1; }
          

          子类,继承父类又定义了相同的函数(不覆盖):

          int foo() { return 2; }
          

          您可以在所有编程语言上执行此操作。没有什么可以阻止此代码编译(编译器上的设置除外)。您将得到的最好的结果是警告您正在隐藏父母的方法。如果你调用子类并调用 foo 方法,你会得到 2。你实际上已经破坏了代码。

          这就是他要问的。

          【讨论】:

            【解决方案8】:

            如果您将子类作为其父类的一种类型,则非虚函数将调用父类的版本。

            即:

            Parent* obj = new Child();
            

            【讨论】:

              【解决方案9】:

              除非您将方法设为虚拟,否则子类不能覆盖它。如果您想阻止子类调用它,请将其设为私有。

              所以默认情况下 C++ 会做你想做的事。

              【讨论】:

                【解决方案10】:

                试图阻止某人在子类中使用与您的函数相同的名称与试图阻止某人使用与您在链接库中声明的相同的全局函数名称没有太大区别。

                您只能希望那些打算使用您的代码而不是其他人的用户会小心他们如何引用您的代码,并希望他们使用正确的指针类型或使用完全限定的范围。

                【讨论】:

                  【解决方案11】:

                  在您的示例中,没有函数被覆盖。相反,它是隐藏的(这是一种退化的重载情况)。 错误在子类代码中。正如 csmba 所建议的,您所能做的就是更改编译器设置(如果可能);只要你不使用隐藏自己功能的第三方库就可以了。

                  【讨论】:

                    【解决方案12】:

                    从技术上讲,您可以防止虚拟功能被覆盖。但是您将永远无法更改或添加更多内容。那是不由得饱了。正如 faq lite 建议的那样,最好在函数前使用注释。

                    【讨论】:

                      【解决方案13】:

                      我正在寻找相同的东西,昨天遇到了这个[相当老的]问题。

                      今天我发现了一个简洁的 c++11 关键字:final。我认为它可能对下一个读者有用。

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

                      【讨论】:

                      • 这个答案没有添加任何其他答案尚未说明的内容;此外,final 只能用于virtual 方法。
                      【解决方案14】:

                      默认情况下,C++ 方法是私有的且不可覆盖。

                      • 您不能覆盖私有方法
                      • 您不能覆盖非virtual 方法

                      你可能是指重载吗?

                      【讨论】:

                      • 我们确实可以覆盖私有虚函数。
                      猜你喜欢
                      • 1970-01-01
                      • 2016-12-12
                      • 1970-01-01
                      • 1970-01-01
                      • 2017-08-27
                      • 2019-10-30
                      • 2020-08-29
                      • 2020-08-02
                      • 1970-01-01
                      相关资源
                      最近更新 更多