在 c ++ 11 中,override
specifier防止不覆盖预期的虚拟基函数(因为签名不匹配)。
final
specifier防止无意中重写派生类中的函数。
= & gt;是否有一个说明符(例如first
或no_override
)可以防止重写未知的基函数?
当虚拟函数被添加到基类时,我想得到一个编译器错误,与派生类中已经存在的虚拟函数具有相同的签名。
编辑 4:为了保持这个问题简单和答案相关,这里再次是
原始伪代码
abstractcl B:A
hasprivate:virtual void fooHasBeenDone() = 0;
cl C:B
implementsprivate:virtual void fooHasBeenDone() override { react();}
Nowcl A
gets a newprivate:virtual void fooHasBeenDone();
但是新的A::foo
可能与原来的B::foo
不同。
和一个具体示例
摘要cl B : A
有virtual void showPath() = 0;
表示 PainterPath
cl C:B
implementsvirtual void showPath() override { mPath.setVisible();}
现在cl A
得到一个新的virtual void showPath();
,表示一个文件路径
现在,当 A 调用 showPath()时,B 显示 painterPath 而不是一些文件路径。
当然这是错误的,然后我应该将B::showPath()
重命名为B::showPainterPath()
并实现B::showPath() override
。
下面是一个编译真实世界的例子:
#include <iostream>
#define A_WITH_SHOWPATH
cl 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
};
cl B : public A
{
public:
virtual void showPath() = 0; // to be called from outside
};
cl C1 : public B {
public:
virtual void showPath() override {
std::cout << "C1 showing painter path as graphic\n";
}
};
cl 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 cl.\n";
std::cout << "---------------------------\n";
#endif
return 0;
}
运行它并查看文本输出。
作为参考,一个具有特定用例(PainterPath 实例)的旧示例:
https://ideone.com/6q0cPD(链接可能已过期)
像first
或no_override
这样的说明符的功能并不存在。可能是因为它可能会造成混乱。但是,可以通过更改方法来轻松实现。
应该使用final
说明符在基类中添加任何新方法。这将有助于获取任何匹配签名的编译器错误。因为,它将使后续派生类方法签名自动作为同类的“第一个”。稍后可以删除final
关键字,因为它仅用于“第一手验证”。
在新添加的基本方法之后放置 & amp;removingfinal
关键字类似于使用 debug (g++ -g
) 选项编译二进制文件,这有助于修复错误。
从你的例子:
cl A {}; // no method, no worry
cl B {
public: virtual void showPath() = 0; // ok
};
...
现在不小心你在添加类似的方法A
,导致错误:
cl A {
public: virtual void showPath() final; // same signature by chance
// remove the `final` specifier once the signature is negotiated
};
cl B {
public: virtual void showPath() = 0; // ERROR
};
因此,必须协商新的A::showPath()
与现有的B::showPath()
之间的签名,然后删除final
说明符。
此答案是社区 wiki,因为它结合了所有其他答案。请投票赞成对您有帮助的特定答案以及此答案。
不,没有像first
或no_override
这样的说明符。(answer)
应尽可能频繁地使用override
说明符。
Qt 有一个macro Q_DECL_OVERRIDE
,可扩展为override
(如果可用)。
如果不可用,至少用注释标记每个覆盖函数。
如果你这样做,有编译器标志警告丢失override
:
"Clang now has -Winconsistent-missing-override
, and newer GCCs have -Wsuggest-override
."
我不知道 VS2012 标志。随意编辑。
您可以通过添加基类无法知道的“秘密”来模仿所需的行为。(answer)
这在非常特定的用例中很有帮助,但通常会虚拟性的概念(请参阅其他答案的注释)。
如果您不拥有基类并且有冲突(例如编译器警告),则需要在所有派生类中重命名虚拟函数。
如果您拥有基类,您可以临时将final
添加到任何新的虚函数中。(answer)
代码编译后没有错误,您知道任何派生类中都不存在该名称和签名的函数,您可以再次删除final
。
...我想我将开始将first虚函数标记为DECL_FIRST
。也许将来会有一种于编译器的检查方法。
不,没有。
将虚函数添加到与子类中的虚函数具有相同签名的基类不能任何现有功能,除非添加该虚函数将基类变成多态类型。因此,在规范中,这是良性的,最纯粹的观点是,添加语言功能来防止这种情况是毫无意义的。
(当然,你可以标记你的新函数final
只是为了检查一个子类函数不会它。)
您唯一的选择是使用代码分析工具。
(请注意,VS2012 不实现,甚至声称实现,C ++ 11 标准,虽然它确实有一些。)
C ++ 似乎没有提供开箱即用的方式。但是你可以像下面这样模仿它:
template<cl Base>
cl Derived : public Base
{
private:
struct DontOverride {};
public:
// This function will never override a function from Base
void foo(DontOverride dummy = DontOverride())
{
}
};
如果你打算引入一个新虚函数,那么做如下:
template<cl Base>
cl Derived : public Base
{
protected:
struct NewVirtualFunction {};
public:
// This function will never override a function from Base
// but can be overriden by subcles of Derived
virtual void foo(NewVirtualFunction dummy = NewVirtualFunction())
{
}
};
本站系公益性非盈利分享网址,本文来自用户投稿,不代表边看边学立场,如若转载,请注明出处
评论列表(9条)