c++中的接口,没有多重继承

Interfaces in C++, WITHOUT multiple inheritance

本文关键字:多重继承 接口 c++      更新时间:2023-10-16

我怀疑这个问题的答案是"不"或"你做错了",但是:

是否有可能实现接口类型的行为不使用继承在c++(11,如果它的关系)?

我有几个不同的结构体,

struct Foo
{
  float A;
  void Bind() 
  { .... }
};
struct Bar
{
  float B;
  void Bind()
  {
  }
};

…和其他

由一个方法操作,该方法将这些结构体的数组传递给另一个进程,并且它们必须非常紧密地打包。如果我使用继承,创建一个实现::Bind()方法的基类,那么后代类不仅拥有它们的数据,而且拥有VMT,这将消耗非常稀缺的资源的很大一部分。其他方法需要对这些不同的类型进行操作,但并不真正关心数据成员或::Bind()方法的细节,这在不同类型之间差异很大。

在c#(或者,我怀疑,java)中,我会这样做:
interface ICommon
{
  void Bind();
}
struct Foo : ICommon
{
  void Bind() { .... };
};
struct Bar : ICommon
{
  void Bind() { ..... }
}

我可以使用模板:

template<typename T>
void Bind(T &item)
{
  item.Bind();
}

,但这引入了一些约束(即,模板需要在头文件中声明,而不是在cpp中,等等),这是不太理想的。我知道有一些技巧可以让你在cpp文件中放置一个模板方法实现,但它们有点乱,我宁愿避免它。

这可能是一个"鱼与熊掌"的请求。

(请注意,这并不是其他c++接口问题的重复,因为我试图避免使用经常推荐的使用多重继承的解决方案。)

您可以使用模板参数获得几乎相同的结果:

template <typename TRAIT>
class ICommon
{
  TRAIT t;
  public: void Bind()
  {
    t.Bind();
  }
}
class FooTrait
{
  public: void Bind() { .... };
};
class BarTrait
{
  public void Bind() { ..... }
}
typedef ICommon<FooTrait> Foo;
typedef ICommon<BarTrait> Bar;
template <typename T>
void call_bind(ICommon<T> x)
{
  x.Bind();
}
int main()
{
  Foo f; Bar b;
  call_bind(f);
  call_bind(b);
}

是否有可能实现接口类型的行为不使用继承在c++(11,如果它的关系)?

是的。封装是继承的可行替代方案。

您使用接口来定义一些行为,然后返回接口(接口仍然是继承的,但不是由您的主类)。

的例子:

class IBinder {
    virtual void Bind() = 0;
};
class Foo: public WhateverBaseClass {
    struct Binder: public IBinder { virtual void Bind() override {} };
    Binder b;
public:
    IBinder& getBinder() { return b; }
};
客户机代码:

Foo f;
f.getBinder().Bind();

如果你真的不想使用模板或继承,你可以使用重载的自由函数:

void Bind(Foo& foo) {}
void Bind(Bar& bar) {}
int main() {
    Foo foo;
    Bar bar;
    Bind(foo);
    Bind(bar);
}

当然,任何需要操作这两种类型的函数都必须重载或模板化。

Java的接口只是一种做坏事的稀释方式,坏事,我发誓,我们永远不会那样做,多重继承。仅此而已。

对于你的问题,如果你想获得一堆共享一个"接口"的对象,按自然的方式做:它们属于接口的类,也就是说,是从它派生出来的。可以创建一个这样的对象的数组(指针指向),甚至稍微注意一下对象本身(但除非绝对必要,否则我不会这么做,因为切掉某些东西的危险太大了)。

Re: "模板只在标题":说谁?只是包含头文件(可能在几个不同的源文件中),以避免一遍又一遍地编写相同的声明(和内联定义)。如果你需要模板,或者类,或者在一个源文件中有什么,没有人会强迫你为此创建一个头文件。