首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >多重继承和纯虚函数

多重继承和纯虚函数
EN

Stack Overflow用户
提问于 2012-01-02 08:10:40
回答 3查看 11.4K关注 0票数 18

以下代码:

代码语言:javascript
代码运行次数:0
运行
复制
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;
}

编译失败,出现以下错误:

代码语言:javascript
代码运行次数:0
运行
复制
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’继承是虚拟的,解决了这个问题,但我不明白为什么。有人能解释一下这是怎么回事吗?

附注:为了使代码更简短,我故意省略了虚拟析构函数。请不要告诉我把它们放进去,我已经知道了:)

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-01-02 08:15:26

在继承树中有两个interface_base基类。这意味着您必须提供两个foo()实现。而且调用它们中的任何一个都会非常笨拙,需要多个强制转换才能消除歧义。这通常不是你想要的。

要解决此问题,请使用虚拟继承:

代码语言:javascript
代码运行次数:0
运行
复制
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提供了一些很好的图表来使这一切变得更加清晰。

票数 16
EN

Stack Overflow用户

发布于 2012-08-02 04:39:27

常用的C++惯用法是:

  • public类的classes
  • private虚拟继承接口的非虚拟继承

在这种情况下,我们将拥有:

代码语言:javascript
代码运行次数:0
运行
复制
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
  • privately通过implementation_base继承:implementation ----> implementation_base --public--> implementation_base

当客户端代码执行这些派生到基础的转换之一时:

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

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

在这里,从implementationinterface_base的转换始终可以由客户端代码通过interface完成;其他无法访问的路径根本无关紧要。从interface_base implementation**.**公开继承了唯一的虚拟库

在许多情况下,实现类(implementationimplementation_base)将对客户端代码隐藏:只有指向接口类(interfaceinterface_base)的指针或引用将被公开。

票数 12
EN

Stack Overflow用户

发布于 2012-01-02 08:41:16

对于“解决”钻石继承问题的情况,bdonlan提供的解决方案是有效的。话虽如此,你可以避免设计中的菱形问题。为什么给定类的每个实例都必须同时被视为两个类?您是否会将这个相同的对象传递给一个类,该类的内容如下:

代码语言:javascript
代码运行次数:0
运行
复制
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实现,而使用更合适的设计则不必如此。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8696473

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档