c++ `override` / `final` 说明符的反义词是什么?

What is the opposite of c++ `override` / `final` specifier?

=> 是否有说明符(可能是 firstno_override)防止覆盖未知的基函数?

我想在将虚函数添加到基础 class 时出现编译器错误,其签名与派生 class 中已有的虚函数具有相同的签名。


编辑 4:为了保持这个问题的简单性和相关性,这里又是

原始伪代码


和一个具体例子

当然这是错误的,我应该将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(link可能已过期)

没有。

将虚函数添加到基 class 中,该基 class 与子 class 中的虚函数具有相同的签名 cannot break 任何 现有功能除非添加虚函数将基础class 转换为多态类型。所以在规范中,它是良性的,最纯粹的人会争辩说,添加语言特性来防止这种情况是毫无意义的。

(当然你可以标记你的新函数 final 只是为了检查子 class 函数不会破坏它。)

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

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

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())
    {
    }
};

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

应该在带有 final 说明符 的基础 class 中添加任何新方法。这将有助于获得任何匹配签名的编译器错误。因为,它将使后续派生的 class 方法签名自动成为同类的 "first"。稍后可以删除 final 关键字 ,因为它仅用于 "first hand verification"。

在新添加的基本方法之后添加和删除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 说明符继续进行。

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

  1. 不,没有像 firstno_override 这样的说明符。
  2. 您应该尽可能多地使用 override 说明符。
    Qt 有一个 macro Q_DECL_OVERRIDE 扩展到 override,如果可用的话。
    如果不可用,至少用注释标记每个覆盖函数。
  3. 如果您这样做,编译器标志会警告缺少 override:

    我不知道用于此的 VS2012 标志。欢迎编辑。
  4. 您可以通过添加基础 class 不知道的 'secret' 来模仿所需的行为。
    这在非常具体的用例中很有用,但通常会打破虚拟性的概念(请参阅对其他答案的评论)。
  5. 如果您不拥有基 class 并且存在冲突(例如编译器警告),您将需要在所有派生 classes.
  6. 如果您拥有基地 class, 您可以临时 添加一个 final 到任何新的虚拟机功能。
    代码编译无误后,你知道任何派生的class中都不存在该名称和签名的函数,你可以再次删除final

...我想我会开始将第一个 虚函数标记为DECL_FIRST。也许将来会有一种独立于编译器的方法来检查这个。