【发布时间】:2009-09-09 22:05:38
【问题描述】:
这是对一些真实代码的简化,当我没有意识到其他人已经实现了 Foo 并从它派生时,我犯了一个真正的错误。
#include <iostream>
struct Base {
virtual ~Base() { }
virtual void print() = 0;
};
struct OtherBase {
virtual ~OtherBase() { }
};
struct Foo : public Base { // better to use virtual inheritance?
virtual void print() { std::cout << "Foo" << std::endl; };
};
struct Bar : public Base { // better to use virtual inheritance?
virtual void print() { std::cout << "Bar" << std::endl; };
};
// The design is only supposed to implement Base once, but I
// accidentally created a diamond when I inherited from Bar also.
class Derived
: public OtherBase
, public Foo
, public Bar // oops.
{
};
int main() {
Derived d;
OtherBase *pO = &d;
// cross-casting
if (Base *pBase = dynamic_cast<Base *>(pO))
pBase->print();
else
std::cout << "fail" << std::endl;
}
编辑:让您不必运行此代码...
- 如果按原样运行,则会显示“失败”(不受欢迎,难以调试)。
- 如果您删除标记为“oops”的行,它会打印“Foo”(期望的行为)。
- 如果您离开“oops”并将两个继承设置为虚拟,它将无法编译(但至少您知道要修复什么)。
- 如果您删除“oops”并将它们设为虚拟,它将编译并打印“Foo”(期望的行为)。
使用虚拟继承,结果要么是好的,要么是编译器错误。如果没有虚拟继承,结果要么是好的,要么是无法解释的、难以调试的运行时故障。
当我实现 Bar 时,它基本上复制了 Foo 已经在做的事情,它导致动态转换失败,这意味着真实代码中的坏事。
起初我很惊讶没有编译器错误。然后我意识到没有虚拟继承,这会触发 GCC 中的“没有唯一的最终覆盖”错误。我故意选择不使用虚拟继承,因为这个设计中不应该有任何菱形。
但是,如果我在从 Base 派生时使用虚拟继承,代码也可以正常工作(没有我的 oops),并且我会在编译时收到有关菱形的警告,而不必在运行时跟踪错误时间。
所以问题是——你认为使用虚拟继承来防止将来犯类似的错误是可以接受的吗?在这里使用虚拟继承没有很好的技术理由(我可以看到),因为设计中不应该有钻石。它只会在那里强制执行该设计约束。
【问题讨论】:
标签: c++ inheritance virtual dynamic-cast diamond-problem