【问题标题】:C++ multiple inheritance / virtual inheritanceC++多重继承/虚拟继承
【发布时间】:2017-03-31 20:01:05
【问题描述】:

对于一个项目,我想从库接口中隐藏一些实现细节。我想出处理这种“高效”(在编码和维护方面)的唯一方法是基于相同抽象基础的抽象和具体基类的多重继承。所以,是的,我最终得到了一个扩展的菱形结构。

我通常会尽可能避免多重继承,因为它很复杂,所以我在使用它方面不是很有经验,所以我不确定我提出的设计是否有一些我不知道的陷阱' t 监督。

基本上我需要提供多个“工具”。这些“工具”当然有一些我想使用接口 ABC 隐藏的实现细节,例如:

类 Tool1Impl : public ITool1;

类 Tool2Impl : public ITool2;

当然,这些工具有很多共同点。

所以我想要一个通用的基类,但是实现细节又应该通过接口隐藏。

我想出了以下(钻石)方案:

            ITool                                           
          /   |   \               
        /     |     \
      /       |       \
ITool1  ToolImplBase   ITool2  ... IToolN
  \       /      \      /
   \     /        \    /
    \   /          \  /
   ToolImpl1      ToolImpl2    ... IToolImplN

ToolImplBase 实现了 ITool 的纯虚拟功能。 ToolImpl1 实现了在 ITool1 中声明的纯虚拟功能。

实现大纲如下:

class ITool {
public:
  virtual int common_foo(int a) = 0;
};

class ITool1 : public virtual ITool {
public:
  virtual int specific_foo(int b) = 0;
};

class ITool2 : public virtual ITool {
public:
  virtual int specific_bar(int c) = 0;
};

class ToolImplBase : public virtual ITool {
public:
  virtual int common_foo(int a) override;
protected:
  int _some_common_data;
};

class ToolImpl1 : public ToolImplBase, public virtual ITool1 {
public:
  virtual int specific_foo(int b) override;
private:
  int _some_specific_data;
};

class ToolImpl2 : public ToolImplBase, public virtual ITool2 {
public:
  virtual int specific_bar(int c) override;
private:
  int _some_specific_data;
};

它似乎做我想做的事,但我不确定这是否有任何潜在的陷阱或明显的问题。我也不确定继承方案中的“虚拟”是否正确(不是虚拟方法,而是“:公共虚拟”)。 一个问题是,MSVC(由于一些外部依赖,我现在坚持使用 2010)给我一个编译器警告 C4250:

warning C4250: 'ToolImpl1': inherits'ToolImplBase::ToolImplBase::common_foo' via dominance

我可以忽略它吗?我什至不确定我为什么会得到这个,因为“common_foo”的非主导方式是纯虚拟的,所以实际上没有有效的“非主导”实现。

感谢您的任何意见(“从不使用 M.I.”除外,我已经尽可能避免使用它)。

【问题讨论】:

  • 我的一点点:在需要时可以随意使用 MI,但是当您拥有钻石时,您可能走错了路。你确定你不是因为结果而不是意义而使用继承吗? ToolImplBase 需要考虑两件事(我不能说更多,因为示例是虚构的):组合和私有继承。
  • 这个例子不是虚构的,它来自我的实际代码,当然是简化的。你会如何在这里使用这个组合?只是将 ToolImplFoo 中 ITool 的所有常用功能委托给包含的 ToolImplBase?
  • C4250函数common_foo被2个路径继承,实现方式不同,需要最终实现
  • “common_foo”的非支配方式是纯虚拟的这正是支配的含义。非纯实现支配纯实现。为什么 MSVC 需要警告它是任何人的猜测。也许它的作者不喜欢或不理解支配规则。其他编译器没有这样的警告。 IMO 你可以放心地忽略这一点,见here,也见here
  • 好吧,警告是有原因的。主导地位可能会在无意中被打破。我宁愿被警告(并决定使警告保持沉默或提供实现)但是,这是一个观点。是的,如果我能避免钻石,我会这样做。我并不是说它更好,只是更不容易出错(IMO)

标签: c++ inheritance interface multiple-inheritance


【解决方案1】:

这里有一些关于警告的解释:What does C4250 VC++ warning mean?

我认为您的代码没有问题,因为您将 ITool1 设计为接口。

如果您在ITool1 中实现common_foo 函数,则在创建ToolImpl1 时,您将不知道common_foo 的哪个实现将起作用。

你会小心这个问题,但其他任何人都可以在IToolX 实现中合法地实现common_foo 函数而不会引发错误。

我最后的评论是:为什么你需要你的ITool1 派生自IToolToolImpl1 已经派生自 ToolImplBase,因此您的实际实现类将始终派生自 ITool

我会改变IToolX界面如下

class IToolX{
    public:
    virtual int specific_foo(int b) = 0;
};

【讨论】:

  • 谢谢,我大致了解 C4250,但不是在这种特殊情况下,因为它实际上并没有在“接口”路径中实现。 - 关于你的问题:我想要从 ITool 派生的 ITool1,因为正如我所说,我想向用户隐藏实现细节(这是一个库)。 “用户”永远不会使用 ToolImplX,而总是使用 IToolX(我可能应该提到构造函数是受保护的,并且我有一个工厂类可以执行以下操作:ITool1* createTool1() { return new ToolImpl1(); }
  • 如果您正在开发项目并且将永远记住您的设计决定,那么就不会有任何问题。但是当其他人在维护代码或几个月后您需要添加新工具时,可能会出现问题。
  • :你会遇到什么样的问题?如果有人因为维护者不了解原始设计而将其撕掉,那么所有代码​​都可能出现问题,这难道不是真的吗?恕我直言,这就是显式代码 cmets 的用途......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-06
  • 2014-10-12
  • 2015-11-21
  • 2010-09-22
  • 2016-03-26
  • 2011-01-08
相关资源
最近更新 更多