9
  • 在 c++11 中,override说明符防止不覆盖预期的虚拟基函数(因为签名不匹配)。
  • 说明final可防止无意中覆盖派生类中的函数。

=> 是否有一个说明符(类似于 Maybefirstno_override)来防止覆盖未知的基函数?

当一个虚函数被添加到一个与派生类中已经存在的虚函数具有相同签名的基类时,我想得到一个编译器错误。


编辑 4:为了使这个问题简单且答案相关,这里又是

原始伪代码

  • 摘要class B : Aprivate: virtual void fooHasBeenDone() = 0;
  • class C : B工具private: virtual void fooHasBeenDone() override { react(); }
  • 现在class A得到一个新的private: virtual void fooHasBeenDone();
  • 但新的A::foo可能与原来的有所不同B::foo

和一个具体的例子

  • 摘要class B : Avirtual void showPath() = 0;一个 PainterPath
  • class C : B工具virtual void showPath() override { mPath.setVisible(); }
  • 现在class A得到一个新的virtual void showPath();含义文件路径
  • 现在,当 A 调用 showPath() 时,B 显示painterPath 而不是某个文件路径。

当然这是错误的,然后我也应该重命名B::showPath()B::showPainterPath()实现B::showPath() override。我只是想从编译器那里得到通知。


这是一个编译的真实示例

#include <iostream>
#define A_WITH_SHOWPATH

class A
{
#ifdef A_WITH_SHOWPATH
public:
    void setPath(std::string const &filepath) {
        std::cout << "File path set to '" << filepath << "'. Display it:\n";
        showPath();
    }
    // to be called from outside, supposed to display file path
    virtual void showPath() {
        std::cout << "Displaying not implemented.\n";
    }
#else
    // has no showPath() function
#endif  
};

class B : public A
{
public:
    virtual void showPath() = 0; // to be called from outside
};

class C1 : public B {
public:
    virtual void showPath() override {
        std::cout << "C1 showing painter path as graphic\n";
    }
};

class C2 : public B {
public:
    virtual void showPath() override {
        std::cout << "C2 showing painter path as widget\n";
    }
};


int main() {
    B* b1 = new C1();
    B* b2 = new C2();

    std::cout << "Should say 'C1 showing painter path as graphic':\n";
    b1->showPath();
    std::cout << "---------------------------\n";
    std::cout << "Should say 'C2 showing painter path as widget':\n";
    b2->showPath();
    std::cout << "---------------------------\n";

#ifdef A_WITH_SHOWPATH
    std::cout << "Should give compiler warning\n or say \"File path set to 'Test'. Display it:\"\n and \"Displaying not implemented.\",\n but not \"C1 showing painter path as graphic\":\n";
    b1->setPath("Test");
    std::cout << "# Calling setPath(\"Test\") on a B pointer now also displays the\n#  PainterPath, which is not the intended behavior.\n";
    std::cout << "# The setPath() function in B should be marked to never override\n#  any function from the base class.\n";
    std::cout << "---------------------------\n";
#endif
    return 0;
}

运行它并查看文本输出。


作为参考,具有特定用例(PainterPath 实例)的旧示例:

https://ideone.com/6q0cPD(链接可能已过期)

4

4 回答 4

3

说明符的功能喜欢firstno_override不存在。可能是因为它可能会造成混乱。但是,通过改变方法可以轻松实现。

final应该使用说明符在基类中添加任何新方法。这将有助于获取任何匹配签名的编译器错误。因为,它会使后续的派生类方法签名自动成为同类中的“第一个”。稍后final可以删除关键字,因为它仅用于“第一手验证”。

在新添加的基本方法之后放置和删除final关键字类似于使用调试(g++ -g)选项编译二进制文件,这有助于您修复错误。在生产中,调试选项被删除以进行优化。

从你的例子:

class A {};  // no method, no worry

class B {
  public: virtual void showPath() = 0;  // ok
};
...

现在不小心在 中添加了类似的方法A,这会导致错误:

class A {
  public: virtual void showPath() final;  // same signature by chance
  // remove the `final` specifier once the signature is negotiated
}; 
class B {
  public: virtual void showPath() = 0;  // ERROR
};

A::showPath()因此,必须协商新的和现有的签名,B::showPath()然后通过删除final说明符来继续。

于 2016-07-29T10:50:22.823 回答
3

这个答案是社区 wiki,因为它结合了所有其他答案。请投票赞成对您以及这个有帮助的具体答案。

  1. 不,没有类似firstor的说明符no_override (回答)
  2. 您应该尽可能多地使用说明override符。如果可用,
    Qt 有一个扩展为的宏。Q_DECL_OVERRIDE 如果不可用,至少用注释标记每个覆盖函数。override
  3. 如果你这样做,会有编译器标志警告缺少override
    Clang now has -Winconsistent-missing-override,并且较新的 GCCs 有-Wsuggest-override
    我不知道 VS2012 标志。随意编辑。
  4. 您可以通过添加基类无法知道的“秘密”来模仿所需的行为。 (答案)
    这在非常具体的用例中很有帮助,但通常会打破虚拟性的概念(请参阅其他答案的评论)。
  5. 如果您不拥有基类并且有冲突(例如编译器警告),您将需要在所有派生类中重命名您的虚函数。
  6. 如果您拥有基类,则可以临时将 a 添加final到任何新的虚函数中。(回答)
    代码编译无误后,你知道在任何派生类中都不存在该名称和签名的函数,你可以final再次删除。

...我想我会开始将第一个虚拟功能标记为DECL_FIRST. 也许将来会有一种独立于编译器的检查方式。

于 2016-08-01T09:14:12.320 回答
2

不,那里没有。

将虚函数添加到与子类中的虚函数具有相同签名的基类不会破坏任何现有功能,除非添加该虚函数会将基类变为多态类型。因此,在规范中,这是良性的,最纯粹的人会争辩说,添加语言功能来防止这种情况是毫无意义的。

(当然你可以标记你的新函数final来检查一个子类函数不会破坏它。)

您唯一的选择是求助于代码分析工具。

(请注意,VS2012 没有实现,甚至没有声称实现 C++11 标准,尽管它确实有一些。)

于 2016-07-29T10:29:58.080 回答
1

C++ 似乎没有提供这种开箱即用的方法。但是你可以像下面这样模仿它:

template<class Base>
class Derived : public Base
{
private:
    struct DontOverride {};

public:
    // This function will never override a function from Base
    void foo(DontOverride dummy = DontOverride())
    {
    }
};

如果您打算引入一个的虚函数,请按如下方式进行:

template<class Base>
class Derived : public Base
{
protected:
    struct NewVirtualFunction {};

public:
    // This function will never override a function from Base
    // but can be overriden by subclasses of Derived
    virtual void foo(NewVirtualFunction dummy = NewVirtualFunction())
    {
    }
};
于 2016-07-29T10:36:27.123 回答