什么是c++ ' override ' / ' final ' specifier的反义词?

时间:2022-03-03 15:44:06
  • In c++11 the override specifier protects from not overriding an intended virtual base function (because the signatures do not match).
  • 在c++11中,覆盖说明符保护不覆盖预期的虚拟基函数(因为签名不匹配)。
  • The final specifier protects from unintentionally overriding a function in a derived class.
  • 最后的说明符防止无意中覆盖派生类中的函数。

=> Is there a specifier (something like maybe first or no_override) that protects from overriding an unknown base function?

=>有一个说明符(类似于first或no_override),它可以保护不覆盖未知的基函数?

I'd like to get a compiler error when a virtual function was added to a base class with the same signature as an already existing virtual function in a derived class.

当一个虚拟函数被添加到一个基类时,我希望得到一个编译器错误,这个基类的签名与派生类中已有的虚拟函数相同。


EDIT 4: To keep this question simple and answers relevant, here is again the

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

original pseudo-code

原始的伪代码

  • abstract class B : A has private: virtual void fooHasBeenDone() = 0;
  • 抽象类B: A具有私有:虚拟void fooHasBeenDone() = 0;
  • class C : B implements private: virtual void fooHasBeenDone() override { react(); }
  • 类C: B实现private: virtual void fooHasBeenDone() override {react();}
  • Now class A gets a new private: virtual void fooHasBeenDone();
  • 现在类A获得一个新的私有:virtual void fooHasBeenDone();
  • But the new A::foo could be something different than the original B::foo.
  • 但是新的A::foo可能与原来的B::foo不同。

and a specific example

和一个具体的例子

  • abstract class B : A has virtual void showPath() = 0; meaing a PainterPath
  • 类B: A具有虚拟void showPath() = 0;意思是PainterPath
  • class C : B implements virtual void showPath() override { mPath.setVisible(); }
  • 类C: B实现虚拟void showPath()覆盖{mPath.setVisible();}
  • Now class A gets a new virtual void showPath(); meaning a file path
  • 现在类A获得了一个新的虚拟void showPath();这意味着文件路径
  • Now when A calls showPath(), B shows the painterPath instead of some file path.
  • 现在,当A调用showPath()时,B显示painterPath而不是某个文件路径。

Of course this is wrong, and I should then rename B::showPath() to B::showPainterPath() and implement B::showPath() override as well. I'd just like to get informed by the compiler.

当然这是错误的,然后我应该将B::showPath()重命名为B:::showPainterPath()并实现B:::showPath()重写。我只想得到编译器的通知。


Here is a compiling real-world example:

下面是一个编译现实世界的例子:

#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;
}

Run it and look at the text output.

运行它并查看文本输出。


For reference, an older example with a specific use-case (PainterPath instance):

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

https://ideone.com/6q0cPD (link may be expired)

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

4 个解决方案

#1


2  

The facility of specifiers like first or no_override is not there as such. Probably because it may create confusion. However, it can trivially be achieved by changing the approach.

诸如first或no_override之类的说明符的功能本身并不存在。可能是因为它会造成混乱。然而,它可以通过改变方法来实现。

One should add any new method in the base class with final specifier. This will help to get the compiler error for any matching signatures. Because, it will make the subsequent derived class method signatures automatically as "first" of their kind. Later the final keyword can be removed, as it was intended just for "first hand verification".

应该在基类中添加任何带有最终说明符的新方法。这将有助于获得任何匹配签名的编译器错误。因为,它将使后续的派生类方法签名自动成为此类的“first”。稍后,最终的关键字可以被删除,因为它只是为了“第一手验证”。

Putting & removing final keyword after the newly added base method is analogically similar to compiling binary with debug (g++ -g) option, which helps you to fix bug. In production that debug option is removed for optimization.

在新添加的基本方法之后放置和删除final关键字,类似于使用debug (g+ -g)选项编译二进制代码,这有助于修复bug。在产品中,为了进行优化,删除了调试选项。

From your example:

从你的例子:

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

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

Now accidentally you are adding similar method in A, that results in error:

现在你不小心在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
};

So the signatures between new A::showPath() & existing B::showPath() have to be negotiated & then carry on by removing final specifier.

因此,new A::showPath()和现有的B::showPath()的签名必须经过协商,然后通过删除最终的说明符进行。

#2


2  

No there is not.

没有没有。

Adding a virtual function to a base class that has the same signature as a virtual function in a child class cannot break any existing functionality unless adding that virtual function turns the base class into a polymorphic type. So in the norm, it's benign, and a purest would argue, adding language features to guard against this would be rather pointless.

向与子类中的虚函数具有相同签名的基类添加虚函数不能破坏任何现有功能,除非添加虚函数将基类转换为多态类型。因此,在通常情况下,它是良性的,最纯粹的人会说,添加语言特性来防范这种情况是相当没有意义的。

(Of course you could mark your new function final just to check that a child class function isn't going to clobber it.)

(当然,你可以标记你的新函数final,只是为了检查一个子类函数是否会阻塞它。)

Your only option is to resort to code analysis tools.

您唯一的选择是使用代码分析工具。

(Note that VS2012 does not implement, or even claim to implement, the C++11 standard, although it does have some of it.)

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

#3


2  

This answer is community wiki because it combines all other answers. Please upvote the specific answer that was helpful to you as well as this one.

这个答案是community wiki,因为它结合了所有其他答案。请把对你和这个问题都有帮助的具体答案向上投赞成票。

  1. No, there is no specifier like first or no_override. (answer)
  2. 不,没有像first或no_override这样的说明符。(回答)
  3. You should use the override specifier as often as possible.
    Qt has a macro Q_DECL_OVERRIDE that expands to override, if available.
    If not available, at least mark each overriding function with a comment.
  4. 您应该尽可能多地使用覆盖说明符。Qt有一个宏Q_DECL_OVERRIDE,如果有的话,扩展到覆盖。如果不可用,至少要用注释标记每个重写函数。
  5. If you do that, there are compiler flags that warn about a missing override:
    "Clang now has -Winconsistent-missing-override, and newer GCCs have -Wsuggest-override."
    I don't know of a VS2012 flag for this. Feel free to edit.
  6. 如果这样做,就会有一些编译器标志来警告丢失的覆盖:“Clang现在有- winconsistency - mis- override,而较新的GCCs有-Wsuggest-override。”我不知道VS2012的标志是什么。请编辑出来。
  7. You can mimic the desired behavior by adding a 'secret' that the base class cannot know. (answer)
    This is helpful in very specific use cases, but generally breaks the concept of virtuality (see comments to the other answers).
  8. 您可以通过添加基类不知道的“秘密”来模拟所需的行为。(答案)这在非常特定的用例中是有用的,但是通常会打破虚拟性的概念(参见对其他答案的评论)。
  9. If you don't own the base class and have a conflict (e.g. compiler warning), you will need to rename your virtual function in all derived classes.
  10. 如果您没有基类并且有冲突(例如编译器警告),您将需要在所有派生类中重命名您的虚拟函数。
  11. If you own the base class, you can temporarily add a final to any new virtual function. (answer)
    After the code compiles without errors, you know that no function of that name and signature exists in any derived class, and you can remove the final again.
  12. 如果您拥有基类,您可以临时向任何新的虚拟函数添加final。(答案)在编译了没有错误的代码之后,您知道在任何派生类中都不存在该名称和签名的函数,您可以再次删除它。

... I think I'll start marking first virtual functions as DECL_FIRST. Maybe in the future there will be a compiler-independent way of checking this.

…我想我要先把第一个虚函数标记为DECL_FIRST。也许将来会有一个编译器独立的检查方法。

#4


1  

C++ doesn't seem to provide such means out of the box. But you can mimic it like follows:

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

If you intend to introduce a new virtual function, then do it like below:

如果您打算引入一个新的虚拟函数,那么如下所示:

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

#1


2  

The facility of specifiers like first or no_override is not there as such. Probably because it may create confusion. However, it can trivially be achieved by changing the approach.

诸如first或no_override之类的说明符的功能本身并不存在。可能是因为它会造成混乱。然而,它可以通过改变方法来实现。

One should add any new method in the base class with final specifier. This will help to get the compiler error for any matching signatures. Because, it will make the subsequent derived class method signatures automatically as "first" of their kind. Later the final keyword can be removed, as it was intended just for "first hand verification".

应该在基类中添加任何带有最终说明符的新方法。这将有助于获得任何匹配签名的编译器错误。因为,它将使后续的派生类方法签名自动成为此类的“first”。稍后,最终的关键字可以被删除,因为它只是为了“第一手验证”。

Putting & removing final keyword after the newly added base method is analogically similar to compiling binary with debug (g++ -g) option, which helps you to fix bug. In production that debug option is removed for optimization.

在新添加的基本方法之后放置和删除final关键字,类似于使用debug (g+ -g)选项编译二进制代码,这有助于修复bug。在产品中,为了进行优化,删除了调试选项。

From your example:

从你的例子:

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

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

Now accidentally you are adding similar method in A, that results in error:

现在你不小心在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
};

So the signatures between new A::showPath() & existing B::showPath() have to be negotiated & then carry on by removing final specifier.

因此,new A::showPath()和现有的B::showPath()的签名必须经过协商,然后通过删除最终的说明符进行。

#2


2  

No there is not.

没有没有。

Adding a virtual function to a base class that has the same signature as a virtual function in a child class cannot break any existing functionality unless adding that virtual function turns the base class into a polymorphic type. So in the norm, it's benign, and a purest would argue, adding language features to guard against this would be rather pointless.

向与子类中的虚函数具有相同签名的基类添加虚函数不能破坏任何现有功能,除非添加虚函数将基类转换为多态类型。因此,在通常情况下,它是良性的,最纯粹的人会说,添加语言特性来防范这种情况是相当没有意义的。

(Of course you could mark your new function final just to check that a child class function isn't going to clobber it.)

(当然,你可以标记你的新函数final,只是为了检查一个子类函数是否会阻塞它。)

Your only option is to resort to code analysis tools.

您唯一的选择是使用代码分析工具。

(Note that VS2012 does not implement, or even claim to implement, the C++11 standard, although it does have some of it.)

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

#3


2  

This answer is community wiki because it combines all other answers. Please upvote the specific answer that was helpful to you as well as this one.

这个答案是community wiki,因为它结合了所有其他答案。请把对你和这个问题都有帮助的具体答案向上投赞成票。

  1. No, there is no specifier like first or no_override. (answer)
  2. 不,没有像first或no_override这样的说明符。(回答)
  3. You should use the override specifier as often as possible.
    Qt has a macro Q_DECL_OVERRIDE that expands to override, if available.
    If not available, at least mark each overriding function with a comment.
  4. 您应该尽可能多地使用覆盖说明符。Qt有一个宏Q_DECL_OVERRIDE,如果有的话,扩展到覆盖。如果不可用,至少要用注释标记每个重写函数。
  5. If you do that, there are compiler flags that warn about a missing override:
    "Clang now has -Winconsistent-missing-override, and newer GCCs have -Wsuggest-override."
    I don't know of a VS2012 flag for this. Feel free to edit.
  6. 如果这样做,就会有一些编译器标志来警告丢失的覆盖:“Clang现在有- winconsistency - mis- override,而较新的GCCs有-Wsuggest-override。”我不知道VS2012的标志是什么。请编辑出来。
  7. You can mimic the desired behavior by adding a 'secret' that the base class cannot know. (answer)
    This is helpful in very specific use cases, but generally breaks the concept of virtuality (see comments to the other answers).
  8. 您可以通过添加基类不知道的“秘密”来模拟所需的行为。(答案)这在非常特定的用例中是有用的,但是通常会打破虚拟性的概念(参见对其他答案的评论)。
  9. If you don't own the base class and have a conflict (e.g. compiler warning), you will need to rename your virtual function in all derived classes.
  10. 如果您没有基类并且有冲突(例如编译器警告),您将需要在所有派生类中重命名您的虚拟函数。
  11. If you own the base class, you can temporarily add a final to any new virtual function. (answer)
    After the code compiles without errors, you know that no function of that name and signature exists in any derived class, and you can remove the final again.
  12. 如果您拥有基类,您可以临时向任何新的虚拟函数添加final。(答案)在编译了没有错误的代码之后,您知道在任何派生类中都不存在该名称和签名的函数,您可以再次删除它。

... I think I'll start marking first virtual functions as DECL_FIRST. Maybe in the future there will be a compiler-independent way of checking this.

…我想我要先把第一个虚函数标记为DECL_FIRST。也许将来会有一个编译器独立的检查方法。

#4


1  

C++ doesn't seem to provide such means out of the box. But you can mimic it like follows:

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

If you intend to introduce a new virtual function, then do it like below:

如果您打算引入一个新的虚拟函数,那么如下所示:

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