以下代码:
struct interface_base
{
virtual void foo() = 0;
};
struct interface : public interface_base
{
virtual void bar() = 0;
};
struct implementation_base : public interface_base
{
void foo();
};
struct implementation : public implementation_base, public interface
{
void bar();
};
int main()
{
implementation x;
}
编译失败,出现以下错误:
test.cpp: In function 'int main()':
test.cpp:23:20: error: cannot declare variable 'x' to be of abstract type 'implementation'
test.cpp:16:8: note: because the following virtual functions are pure within 'implementation':
test.cpp:3:18: note: virtual void interface_base::foo()
我已经尝试过了,我想让'interface -> interface_base‘和'implementation_base -> interface_base’继承是虚拟的,解决了这个问题,但我不明白为什么。有人能解释一下这是怎么回事吗?
附注:为了使代码更简短,我故意省略了虚拟析构函数。请不要告诉我把它们放进去,我已经知道了:)
发布于 2012-01-02 08:15:26
在继承树中有两个interface_base
基类。这意味着您必须提供两个foo()
实现。而且调用它们中的任何一个都会非常笨拙,需要多个强制转换才能消除歧义。这通常不是你想要的。
要解决此问题,请使用虚拟继承:
struct interface_base
{
virtual void foo() = 0;
};
struct interface : virtual public interface_base
{
virtual void bar() = 0;
};
struct implementation_base : virtual public interface_base
{
void foo();
};
struct implementation : public implementation_base, virtual public interface
{
void bar();
};
int main()
{
implementation x;
}
使用虚拟继承,对于所有虚拟提及,只在继承继承层次结构中创建所讨论的基类的一个实例。因此,只有一个implementation_base::foo()
可以满足的foo()
。
有关更多信息,see this prior question - answers提供了一些很好的图表来使这一切变得更加清晰。
发布于 2012-08-02 04:39:27
常用的C++惯用法是:
在这种情况下,我们将拥有:
struct interface_base
{
virtual void foo() = 0;
};
struct interface : virtual public interface_base
{
virtual void bar() = 0;
};
struct implementation_base : virtual public interface_base
{
void foo();
};
struct implementation : private implementation_base,
virtual public interface
{
void bar();
};
在implementation
中,唯一的interface_base
虚拟库是:
interface
公开继承:implementation
--public--> interface
--public--> interface_base
implementation_base
继承:implementation
----> implementation_base
--public--> implementation_base
当客户端代码执行这些派生到基础的转换之一时:
重要的是,从派生类到给定基类子对象至少有一条可访问的继承路径;其他不可访问的路径被忽略。因为基类的继承在这里只是虚的,所以只有一个基类主题,所以这些转换永远不会有歧义。
在这里,从implementation
到interface_base
的转换始终可以由客户端代码通过interface
完成;其他无法访问的路径根本无关紧要。从interface_base
implementation
**.**公开继承了唯一的虚拟库
在许多情况下,实现类(implementation
,implementation_base
)将对客户端代码隐藏:只有指向接口类(interface
,interface_base
)的指针或引用将被公开。
发布于 2012-01-02 08:41:16
对于“解决”钻石继承问题的情况,bdonlan提供的解决方案是有效的。话虽如此,你可以避免设计中的菱形问题。为什么给定类的每个实例都必须同时被视为两个类?您是否会将这个相同的对象传递给一个类,该类的内容如下:
void ConsumeFood(Food *food);
void ConsumeDrink(Drink *drink);
class NutritionalConsumable {
float calories() = 0;
float GetNutritionalValue(NUTRITION_ID nutrition) = 0;
};
class Drink : public NutritionalConsumable {
void Sip() = 0;
};
class Food : public NutritionalConsumable {
void Chew() = 0;
};
class Icecream : public Drink, virtual public Food {};
void ConsumeNutrition(NutritionalConsumable *consumable) {
ConsumeFood(dynamic_cast<Food*>(food));
ConsumeDrink(dynamic_cast<Drink*>(drink));
}
// Or moreso
void ConsumeIcecream(Icecream *icecream) {
ConsumeDrink(icecream);
ConsumeFood(icecream);
}
当然,在这种情况下,对于Icecream
来说,更好的方法是实现NutritionalConsumable
,并提供一个GetAsDrink()
和GetAsFood()
方法,该方法将返回一个代理,纯粹是为了表现为食物或饮料。否则,这意味着有一个接受Food
的方法或对象,但不知何故希望稍后将其视为Drink
,这只能通过dynamic_cast
实现,而使用更合适的设计则不必如此。
https://stackoverflow.com/questions/8696473
复制相似问题