繁体   English   中英

多重继承和纯虚函数

[英]Multiple inheritance and pure virtual functions

以下代码:

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'继承虚拟,修复问题,但我不明白为什么。 有人可以解释一下发生了什么吗?

ps我故意省略了虚拟析构函数以缩短代码。 请不要告诉我把它们放进去,我已经知道:)

继承树中有两个 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;
}

对于虚拟继承,在继承层次结构中只为所有虚拟提及创建了一个有问题的基类实例。 因此,只有一个foo() ,可以通过implementation_base::foo()来满足。

有关更多信息, 请参阅此先前的问题 - 答案提供了一些很好的图表,使这一切更加清晰。

通常的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 --private - > implementation_base --public - > interface_base

当客户端代码执行其中一个派生到基本转换时:

  • 派生到基指针转换,
  • 基类型的引用绑定与派生的静态类型的初始化程序,
  • 通过派生静态类型的左值访问继承的基类成员,

重要的只是从派生类到给定的基类子对象至少有一个可访问的继承路径; 其他不可访问的路径被简单地忽略。 因为基类的继承只在这里是虚拟的,所以只有一个基类主题,因此这些转换永远不会模糊。

这里,从implementationinterface_base的转换总是可以通过interface通过客户端代码完成; 另一条无法进入的道路根本不重要。 唯一的interface_base虚拟基础是从implementation公开继承的。

在许多情况下,实现类( implementationimplementation_base )将保持对客户端代码的隐藏:仅暴露指针或接口类( interfaceinterface_base )的引用。

对于“解决”钻石继承问题的情况,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()方法会GetAsFood()返回代理,这纯粹是出于食物或饮料的目的。 否则,这表明有一种方法或对象接受Food但不知何故想稍后将其视为Drink ,这只能通过dynamic_cast来实现,而不需要采用更合适的设计。

暂无
暂无

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

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