【发布时间】:2016-12-04 00:44:27
【问题描述】:
- 在 c++11 中,
overridespecifier 防止不覆盖预期的虚拟基函数(因为签名不匹配)。 -
finalspecifier 可防止无意中覆盖派生类中的函数。
=> 是否有一个说明符(可能是first 或no_override)来防止覆盖未知的基函数?
当一个虚函数被添加到一个与派生类中已经存在的虚函数具有相同签名的基类时,我想得到一个编译器错误。
编辑 4:为了让这个问题简单且答案相关,这里又是
原始伪代码
- 抽象的
class B : A有private: virtual void fooHasBeenDone() = 0; -
class C : B实现private: virtual void fooHasBeenDone() override { react(); } - 现在
class A得到一个新的private: virtual void fooHasBeenDone(); - 但新的
A::foo可能与原来的B::foo不同。
还有一个具体的例子
- abstract
class B : A有virtual 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(链接可能已过期)
【问题讨论】:
-
也许
-Wsuggest-overrideGCC 选项可能会有所帮助,请参阅gcc.gnu.org/onlinedocs/gcc/Warning-Options.html -
恕我直言,这是一个更多关于设计的问题。正如 Bathsheba 提到的,除了在基类中使用 final 是不可能的。或者您可以使用 Leon 提到的技巧,它破坏了接口类目的 (see here) 并解决了其他一些问题。正如您在上次编辑中提到的那样,您不应该将相同的函数命名为不同的含义。如果您想获得通知,请使用一些分析工具(Bathsheba imho 也提到过这里唯一的正确答案)。 (恕我直言)
-
假设 A 类位于派生类的单独标头中,您可以使用 #define SetPath #error #include "classA.h" #undef SetPath 之类的内容。这将阻止将任何 SetPath 函数添加到 A,但会在将 SetPath 添加到 A 之前构建而不会出错。我并不是说它很好。如果我把它作为一个答案,我希望有些人会因为它使用#define 而投反对票。
-
您对新代码造成了更多混乱! :-) 每次您更改代码时,都会使我们回答中从您的 Q 中获取的示例无效。为什么使用术语“
B的实例”?它令人困惑,因为B由于纯virtual而不能有实例。另外,PainterPath必须在这里做什么?你的最后一句话令人困惑。我建议不要将实际代码与构造函数等放在一起,而只需放置最小的部分,这是独立的,任何人都可以使用 ideone.com 进行验证。如果不满足您的需求,您也可以查看我的答案和评论。 -
@iammilind “你为什么使用术语“B的实例”?它令人困惑,因为B由于纯虚拟而不能有实例” OO中的AFAIU通常是a的实例子类也是基类的一个实例。将“B 的实例”读作“B 的实例(包括从 B 派生的任何类)”。
标签: c++ c++11 inheritance class-hierarchy modifiers