在 C++ 中抽象容器

Posted

技术标签:

【中文标题】在 C++ 中抽象容器【英文标题】:Abstracting containers in C++ 【发布时间】:2021-12-31 10:05:48 【问题描述】:

我想要一个包含返回可迭代对象(容器?)的方法的虚拟基类(接口)。实现类将各自处理它们的容器,例如,A 类将容器实现为向量,B 类实现为列表等。

基本上,我想做这样的事情:

class Base
  virtual Container getContainer() = 0;


class A:Base
  Vector v;
  Container getContainer() return v;


A a;
Iterator begin = a.getContainer().begin;
Iterator end = a.getContainer().end;

如中,调用者将负责处理迭代器(例如调用开始和结束函数进行迭代)

我认为这样的事情是可能的,但我不知道该怎么做。具体来说,我假设 vector 和 list 等类继承自定义方法 begin()end() 的通用接口,但我无法弄清楚该接口是什么以及如何处理它。

【问题讨论】:

查找类型擦除。 查看***.com/questions/22350712/… 或softwareengineering.stackexchange.com/questions/386614/… 您不能以这种方式抽象标准容器,因为它们没有通用的基类。容器的规范基于每个容器提供一个接口(例如一组特定的成员函数),但是,尽管所有接口中都有一些共同的元素,但不同的容器没有相同的接口 - 并且没有共同的基类标准容器(在 c++ 标准库中)和用户定义的容器需要继承自。 【参考方案1】:

查看 cppreference.com。这个通用接口只是一个约定和一些规则,而不是实际的 C++ 类型。所以不,你不能多态地使用容器。简单的原因是效率,运行时多态性会降低性能。如需更详细的了解,有很多关于 STL 背后的设计理念的文档,其中有所体现。

如果你真的想实现这一点,你不仅需要围绕 STL 风格的序列容器(deque、vector、list、array)的包装类,还需要围绕迭代器的包装类。迭代器通常与容器硬绑定,因此有这种连接。包装类还必须提供您在底层容器中找到的各种方法,例如begin()end()size()

请注意,并非每个容器都支持所有方法。例如,有些不支持push_front(),而只支持push_back()。它们都有begin()end(),但是单链表(目前不是C++ 的一部分)不能有rbegin()rend()。您可以将这些类别分类为单独的纯虚拟基类。

总的来说,我怀疑这是否有用。如果您想交换实现,请设计您的代码,使容器成为它的模板参数。然后在编译时解析所有调用,从而减少代码、更少的内存需求和更高的性能。

【讨论】:

【参考方案2】:

容器不是多态的。没有通用的基类。迭代器也是如此。

这是有意的,因为当通过基于虚拟方法的接口完成时,逐个元素的迭代被证明是非常低效的。可以,但是速度很慢。

Boost 具有任何范围和任何迭代器,或者您可以使用自己的 vis 类型擦除技术。我建议不要这样做。

获得迭代多态的最简单和最便宜的方法是添加一个foreach_element(std::function<void(Element const&)>)const 方法。您可以使用foreach_element(std::function<void(std::span<Element const>)>)const 批量迭代以减少开销,允许元素被容器聚集在一起。这将比完全多态的迭代器和容器更容易编写和更快。

【讨论】:

以上是关于在 C++ 中抽象容器的主要内容,如果未能解决你的问题,请参考以下文章

58.抽象数据类型

C++泛型和算法

C++泛型基础

C++面向对象:C++ 接口(抽象类)

设计模式 C++抽象工厂模式

C++ 抽象类二(抽象类的基本语法)