为什么C++不允许基类实现派生类的继承接口?

Joh*_*itb 13 c++ inheritance interface class

这就是我所说的

// some guy wrote this, used as a Policy with templates
struct MyWriter {
  void write(std::vector<char> const& data) {
    // ...
  }
};
Run Code Online (Sandbox Code Playgroud)

在一些现有代码中,人们不使用模板,而是使用接口+类型擦除

class IWriter {
public:
  virtual ~IWriter() {}

public:
  virtual void write(std::vector<char> const& data) = 0;
};
Run Code Online (Sandbox Code Playgroud)

其他人希望可以使用两种方法和写入

class MyOwnClass: private MyWriter, public IWriter {
  // other stuff
};
Run Code Online (Sandbox Code Playgroud)

MyOwnClass是MyWriter实现的.为什么MyOwnClass的继承成员函数不能自动实现IWriter的接口?相反,用户必须编写转发函数,除了调用基类版本之外什么都不做,如

class MyOwnClass: private MyWriter, public IWriter {
public:
  void write(std::vector<char> const& data) {
    MyWriter::write(data);
  }
};
Run Code Online (Sandbox Code Playgroud)

我知道在Java中,当你有一个实现接口的类并从一个碰巧有合适方法的类派生时,该基类会自动实现派生类的接口.

为什么C++不这样做?这似乎是一件很自然的事情.

Ben*_*igt 15

这是多重继承,并且有两个具有相同签名的继承函数,两者都具有实现.这就是C++与Java不同的地方.

因此,调用write静态类型的表达式对于MyBigClass所需的继承函数将是不明确的.

如果write只通过基类指针调用,那么write在派生类中定义是不必要的,这与问题中的声明相反. 既然问题已经改为包含一个纯粹的说明符,那么在派生类中实现该函数对于使该类具体和可实例化是必要的.

MyWriter::write不能用于虚拟调用机制MyBigClass,因为虚拟调用机制需要一个接受隐式的函数IWriter* const this,并MyWriter::write接受一个隐式MyWriter* const this.需要一个新函数,它必须考虑IWriter子对象和MyWriter子对象之间的地址差异.

从理论上讲,编译器可以自动创建这个新函数,但它很脆弱,因为基类的更改可能会突然导致选择新函数进行转发.它在Java中不那么脆弱,只有单一继承是可能的(转发的函数只有一个选择),但在支持完全多重继承的C++中,选择是模糊的,我们甚至没有开始钻石继承还是虚拟继承.

实际上,这个问题(子对象地址之间的差异)解决了虚拟继承.但它需要额外的开销,这在大多数时候都是不必要的,而C++的指导原则是"你不为你不使用的东西买单".

  • 为什么它是虚拟的呢?是什么阻止了规则进入C++标准,使案例格式良好? (2认同)

Sig*_*erm 5

为什么C++不这样做?这似乎是一件很自然的事情.

实际上,不,这是非常不自然的事情.

请注意,我的推理是基于我自己对"常识"的理解,因此可能会产生根本性的缺陷.

你看,你有两种不同的方法,第一种是MyWriter,非虚拟,第二种是IWriter,它是虚拟的.尽管"看起来"相似但它们完全不同.

我建议查看这个问题.非虚方法的好处在于,无论你做什么,只要它们不调用虚方法,它们的行为就永远不会改变.即,使用非虚方法从您的类派生的人不会通过屏蔽它们来破坏现有方法.虚拟方法旨在被覆盖.这样做的代价是可以通过不正确地覆盖虚拟方法来破坏底层逻辑.这是你问题的根源.

让我们说你的建议是允许的.(自动转换为具有多重继承的虚拟)有两种可能的解决方案:

解决方案#1 MyWriter变为虚拟.后果:世界上所有现有的C++代码都很容易通过拼写错误或名称冲突来破解.MyWriter方法最初不应该被覆盖,因此当有人派生自MyOwnClass时,突然将其转换为虚拟意志(墨菲定律)会破坏MyWriter类的基础逻辑.这意味着突然制作MyWriter :: write virtual是个坏主意.

Soluion #2 MyWriter保持静态BUUUT它暂时作为虚拟方法包含在IWriter中,直到覆盖.乍一看没有什么可担心的,但让我们考虑一下.IWriter实现了你想到的某种概念,它应该做一些事情.MyWriter实现了另一个概念.要将MyWriter :: write指定为IWriter :: write方法,您需要两个保证:

  1. 编译器必须确保MyWriter :: write执行IWriter :: write()应该执行的操作.
  2. 编译器必须确保从IWriter调用MyWriter :: write不会破坏程序员希望在别处使用的MyWriter代码中的现有功能.

所以,问题是编译器无法保证.函数具有相似的名称和参数列表,但是根据墨菲定律,这意味着它们可能做了完全不同的事情.(例如,sinf和cosf具有相同的参数列表),编译器不太可能预测未来,并确保在开发过程中不会以任何方式更改MyWriter,使其与IWriter不兼容.因此,由于机器本身无法做出合理的决定(没有AI),它必须问你,程序员 - "你想做什么?".你说"将虚方法重定向到MyWriter :: write().它完全不会破坏任何东西.我想."

这就是为什么你必须指定你想要手动使用哪种方法....