Inheritance-free polymorphism

Inheritance-free polymorphism

本文关键字:polymorphism Inheritance-free      更新时间:2023-10-16

X:我有一个不相关的容器类对象(向量,映射,树,…)的集合,处理不同不相关类型的对象。这些容器中对象的生命周期在它们的某些子集之间共享。我有一个对象负责它们的同步。我能想到的最简单的同步类实现是有一个BaseContainerLike指针的向量,其中BaseContainerLike将是一个为我想要管理的所有容器类对象实现公共接口的类。但并不是所有的都像容器一样。它们可以像容器一样使用,但是让它们从公共基类继承感觉非常奇怪,我担心它会非常强烈地耦合我的设计。

所以我创建了一个ContainerLikeInterface类,像这样:

struct ContainerLikeInterface {
template<T>
ContainerLikeInterface(T& t) 
 : create([](int i){ return t->create(i); }),    // this is just an example
   delete([](int i){ return t->delete(i); }) {}
std::function<void(int)> create;
std::function<void(int)> delete;
};
template<class T>
ContainerLikeInterface make_containerLikeInterface(T& t) {
  return ContainerLikeInterface(t);
}

这允许我以一种非侵入性的方式简单地创建一个接口向量(我可以为不同的类型部分专门化构造函数)。使用这种方法的代码比使用继承时稍微快一些,但是它需要更多的内存和更长的编译时间(但我没有优先考虑编译时间)。然而,我不知道这种方法是否可以很好地扩展到我的项目中。我读过一些关于价值语义的文章,其中人们更喜欢将对象所有权转移到接口,所以我有以下问题:

  • 这种方法的优缺点是什么?
  • 从长远来看,这会给我带来一些问题吗?
  • 我应该使用继承吗?
  • 我应该以不同的方式实现这一点吗?
  • 我应该用库代替吗?(boost::TypeErasure, adobe::poly,或pyrtsa/poly)

你的基本想法(不需要继承)是好的。我建议使用Adobe。聚。当您每个操作使用1个std::function时,您有N种虚拟表(指针),加上可能的N个堆分配(取决于是否可以应用SBO(小缓冲区优化))。

您也很可能遇到对象生命周期管理问题。在你的实现中,你假设真正的对象比"接口"存在的时间更长。你迟早会弄错的。这就是我鼓励价值语义方法的原因。Adobe。Poly给你这个

与Adobe

。多边形你只得到一个虚表(指针)。它还实现了SBO:可能不是单个分配。

我不一定会使用Boost.TypeErasure。它需要学习另一种"语言"来指定接口,这利用了大量的元编程,到目前为止,它还没有实现SBO。

Adobe。Poly没有很好的文档记录。关于如何使用它的示例,请参阅这篇文章。另外,请参阅这篇文章,了解它是如何实现的。

您的接口与中创建的接口系统略有相似Rust对象系统。经典的基于VMT的接口有一个指向对象的指针,该对象持有指向VMT的指针。你有两个指针:一个指向对象,另一个指向方法表。它(几乎)总是看起来比你已经提到的缺点(内存使用等)的虚拟函数更强大。至于速度,std::function使用标准分配器来保持指向t的指针。如果你经常调用ContainerLikeInterface构造函数,它可能会导致一些性能下降,因为在你的接口中,每个std::function至少需要一个分配,你可以自己写一个。

您实际上是在创建一个具有ContainerLikeInterface接口的代理对象,该对象带有指向某些T的指针或引用。

有一种方法可以避免创建代理,同时仍然使用不能派生ContainerLikeInterface的标准容器。它可能被称为混合设计模式,在c++ 11中使用完美的转发,它看起来像:
#include <vector>
struct IContainerLikeInterface
{
    virtual void create(int) = 0;
    virtual void erase(int) = 0;
};
template<class T>
struct ContainerLikeInterface : T, IContainerLikeInterface
{
    template<class... Args>
    ContainerLikeInterface(Args&& ...args) 
        : T(std::forward<Args>(args)...)
    {}
    // implement IContainerLikeInterface
    void create(int) override;
    void erase(int) override;
};
int main() {
    ContainerLikeInterface<std::vector<int> > v(10);
    v.size();
}

然而,它是侵入性的,因为所讨论的容器的所有声明,如std::vector<int>,都必须更改为ContainerLikeInterface<std::vector<int> >。用法保持不变,因为ContainerLikeInterface<T>是-a T