1

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::TypeErasureadobe::polypyrtsa/poly
4

3 回答 3

1

你的界面有点像 Rust object system中的界面系统。经典的基于 VMT 的接口有一个指向对象的指针,该对象包含指向 VMT 的指针。你的有 2 个指针:一个指向对象,另一个指向方法表。它 [几乎] 总是看起来比虚函数更强大,但你已经提到了一些缺点(内存使用等)。至于速度,std::function使用标准分配器来保存指向t. 如果你调用ContainerLikeInterface构造函数很多,它可能会导致一些性能下降,因为它需要在你的界面中至少一个分配std::function,你可以为此编写自己的。

于 2013-03-07T21:15:59.380 回答
1

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

您也很可能会遇到对象生命周期管理问题。在您的实现中,您假设真实对象的寿命比“接口”长。迟早你会弄错的。这就是为什么我会鼓励一种价值语义的方法。Adobe.Poly 为您提供。

使用 Adob​​e.Poly,您只能获得一个 vtable(指针)。它还实现了 SBO:可能不是单个分配。

我不一定会选择 Boost.TypeErasure。它需要学习另一种“语言”来指定接口,它利用了大量的元编程,并且截至今天它还没有实现 SBO。

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

于 2014-12-03T15:08:40.603 回答
0

您实际上是在创建一个代理对象,其ContainerLikeInterface接口带有指针或对 some 的引用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>is-a T

于 2013-03-06T19:33:27.583 回答