简体   繁体   English

与c ++`override` /`final`说明符相反的是什么?

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

  • In c++11 the override specifier protects from not overriding an intended virtual base function (because the signatures do not match). 在c ++ 11中, override说明符可以防止不覆盖预期的虚拟基本函数(因为签名不匹配)。
  • The final specifier protects from unintentionally overriding a function in a derived class. final说明符可防止无意中覆盖派生类中的函数。

=> Is there a specifier (something like maybe first or no_override ) that protects from overriding an unknown base function? =>是否有一个说明符(类似于firstno_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; 抽象class B : Aprivate: virtual void fooHasBeenDone() = 0;
  • class C : B implements private: virtual void fooHasBeenDone() override { react(); } class C : B实现private: virtual void fooHasBeenDone() override { react(); } private: virtual void fooHasBeenDone() override { react(); }
  • Now class A gets a new private: virtual void fooHasBeenDone(); 现在, class A获得一个新的private: 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; 抽象class B : A具有virtual void showPath() = 0; meaing a PainterPath 使用PainterPath
  • class C : B implements virtual void showPath() override { mPath.setVisible(); } class C : B实现virtual void showPath() override { mPath.setVisible(); } virtual void showPath() override { mPath.setVisible(); }
  • Now class A gets a new virtual void showPath(); 现在, class A获得一个新的virtual void showPath(); meaning a file path 意思是文件路径
  • 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. 当然这是错误的,然后我应该将B::showPath()重命名为B::showPainterPath()并实现B::showPath() override I'd just like to get informed by the compiler. 我只想得到编译器的通知。


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 (链接可能已过期)

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标准,尽管它确实有一些。)

The facility of specifiers like first or no_override is not there as such. firstno_override这样的说明符的工具不存在。 Probably because it may create confusion. 可能是因为它可能会造成混乱。 However, it can trivially be achieved by changing the approach. 但是,通过改变方法可以很容易地实现。

One should add any new method in the base class with final specifier . 应该使用final说明符在基类中添加任何新方法 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". 稍后可以删除final关键字 ,因为它仅用于“第一手验证”。

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. 在新添加的基本方法之后放置和删除final关键字类似于使用debug( g++ -g )选项编译二进制文件,这有助于您修复错误。 In production that debug option is removed for optimization. 在生产中,删除调试选项以进行优化。

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. 因此,必须协商新的A::showPath()和现有的B::showPath()之间的签名,然后通过删除final说明符来继续。

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. 请upvote特定的答案,这对你和这个有帮助。

  1. No, there is no specifier like first or no_override . 不,没有像firstno_override这样的说明no_override (answer) (回答)
  2. You should use the override specifier as often as possible. 您应该尽可能频繁地使用override说明符。
    Qt has a macro Q_DECL_OVERRIDE that expands to override , if available. Qt有一个Q_DECL_OVERRIDE ,如果可用,它会扩展为override
    If not available, at least mark each overriding function with a comment. 如果不可用,至少用注释标记每个覆盖功能。
  3. If you do that, there are compiler flags that warn about a missing override : 如果你这样做,有一些编译器标志警告缺少override
    " Clang now has -Winconsistent-missing-override , and newer GCCs have -Wsuggest-override . " Clang现在有-Winconsistent-missing-override ,而较新的GCC有-Wsuggest-override
    I don't know of a VS2012 flag for this. 我不知道VS2012标志。 Feel free to edit. 随意编辑。
  4. 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). 这在非常具体的用例中很有用,但通常会破坏虚拟的概念(请参阅其他答案的注释)。
  5. If you don't own the base class and have a conflict (eg compiler warning), you will need to rename your virtual function in all derived classes. 如果您不拥有基类并且存在冲突(例如编译器警告),则需要在所有派生类中重命名虚函数。
  6. If you own the base class, you can temporarily add a final to any new virtual function. 如果您拥有基类,则可以临时向任何新虚函数添加final (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. 代码编译后没有错误,您知道任何派生类中都不存在该名称和签名的功能,您可以再次删除该final

... I think I'll start marking first virtual functions as DECL_FIRST . ...我想我会开始将第一个虚拟函数标记为DECL_FIRST Maybe in the future there will be a compiler-independent way of checking this. 也许在将来会有一种独立于编译器的方法来检查它。

C++ doesn't seem to provide such means out of the box. C ++似乎没有提供开箱即用的手段。 But you can mimic it like follows: 但你可以模仿它如下:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM