当前位置: 首页 > 知识库问答 >
问题:

无继承多态性

章飞章
2023-03-14

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);
}

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

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

共有3个答案

李俊雅
2023-03-14

实际上,您是在创建一个代理对象,该代理对象具有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

李辉
2023-03-14

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

夏建弼
2023-03-14

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

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

使用Adobe。多边形您只会得到一个vtable(指针)。它还实现SBO:可能不是单个分配。

我不一定会选择Boost。类型擦除。它需要学习另一种“语言”来指定接口,这种语言利用了大量的元编程,而到目前为止,它还没有实现SBO。

土砖Poly没有很好的文档记录。有关如何使用它的示例,请参见本文。此外,请参阅本文,了解它是如何实现的。

 类似资料:
  • 问题内容: 假设我有两个班级:动物和狗。狗是动物的子类。我执行以下代码: 现在,我可以通过a变量来调用Dog类的方法。 但是我的问题是:如果我可以通过Dog对象(继承性)调用Animal的所有方法,那么为什么要使用多态原理呢?我可以声明: 通过此声明,可以使用所有Animal方法和Dog方法。那么为什么要使用多态呢?非常感谢您的回答。 问题答案: 在Java中,多态和继承的概念被“焊接在一起”。通

  • 在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。 比如,我们已经编写了一个名为Animal的class,有一个run()方法可以直接打印: class Animal(object): def run(self):

  • 在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。 比如,我们已经编写了一个名为Animal的class,有一个run()方法可以直接打印: class Animal(object): def run(self):

  • 在面向对象编程中,当我们已经创建了一个类,而又想再创建一个与之相似的类,比如添加几个方法,或者修改原来的方法,这时我们不必从头开始,可以从原来的类派生出一个新的类,我们把原来的类称为父类或基类,而派生出的类称为子类,子类继承了父类的所有数据和方法。 让我们看一个简单的例子,首先我们定义一个 Animal 类: class Animal(object): def __init__(self,

  • 问题内容: 继承和多态都构成IS- A关系吗?在运行时发生继承和“重写”多态性而在编译时发生“过载”多态性是真的吗?我之所以这样问,是因为许多论坛似乎给出了相互矛盾且常常令人困惑的答案。 谢谢! 问题答案: 对于问题的第一部分,我认为维基百科提供了一个很好的定义: 在面向对象的程序设计中,子类型多态或包含多态是类型理论中的一个概念,其中名称可以表示许多不同类的实例,只要它们与某个公共超类相关即可。

  • 主要内容:多继承下的构造函数,命名冲突在前面的例子中,派生类都只有一个基类,称为 单继承(Single Inheritance)。除此之外, C++也支持 多继承(Multiple Inheritance),即一个派生类可以有两个或多个基类。 多继承容易让代码逻辑复杂、思路混乱,一直备受争议,中小型项目中较少使用,后来的 Java、 C#、 PHP 等干脆取消了多继承。 多继承的语法也很简单,将多个基类用逗号隔开即可。例如已声明了类A