【问题标题】:Efficient container for Object and Subclass对象和子类的高效容器
【发布时间】:2021-08-05 06:33:28
【问题描述】:

我需要什么

我需要一个容器来存储对象A 和派生子类B 的实例。具体来说,我需要实现以下任务:

  • 高效添加新实例(追加即可)。
  • 在容器中的随机位置高效删除实例(无需按索引查找;在迭代容器时会删除对象)。
  • 对容器中的元素进行高效迭代。这是最重要的部分,因为迭代比操作更频繁。

示例

头文件可能如下所示:

int globalB = 5;

// Base class
class A {
public:
    A(a) : a(a);
    ~A();

    int a;

    virtual int get_b() {
        return globalB;
    }
};

// Derived class
class B : public A {
public:
    B(a, b) : A(a), b(b);
    ~B();

    int a;
    int b;

    int get_b() {
        return b;
    }
};

// Container
class Container {
public:
    Container();
    ~Container();

    // adds an A element
    void add_element(a);

    // adds a B element
    void add_element(a, b);

    // removes all elements with (elem.a == 0)
    void remove_a0_elements();

    // Iterator (I will still have to figure out how this is done properly)
    struct Iterator { /* ... */ };
};


static int example_usage() {

    auto container = Container();
    for (int a=1; i<=100; i++) {
        container.add_element(a);
        container.add_element(a, a);
    }

    int sum = 0;
    for (auto &elem : container) {
        sum += elem.get_b();
    }

    return sum;
}

请注意,与示例所建议的不同,元素不会在连续操作中添加,而是在程序中随机添加。当然,我用来完成示例中任务的容器的任何结构也都很好(例如,通过移交它而不是就地构建它来添加一个元素)。如果有一些内存开销,那不是主要问题,因为所有对象加起来都不是很大。

到目前为止我的想法

我考虑过使用std::unique_ptr 的向量来完成任务,正如here 所建议的那样。但是,我担心内存会以这种方式分散,从而大大降低迭代的性能(请参阅here)。另一个想法是让Container 包装两个向量——分别是A 和B——但是我不知道如何构建迭代器。此外,这将使使用更多子类变得困难(我需要它至少适用于两对基类和子类)。

问题

  • 是否有任何标准容器可以满足我的需求?
  • 如果不是,根据需要实现容器的优雅方式是什么?
  • 有没有办法在不知道大小的情况下“保留”一块内存来构造Container 的元素?然后我可以包装一个指针向量并规避分散内存的问题。

【问题讨论】:

  • std::vector&lt; std::unique_ptr&lt; A &gt; &gt; 怎么样?
  • @RichardCritten 不会因为内存分散而导致问题吗? (见问题)
  • 对此我不是 100% 确定,但我想我已经得到了关于保留内存部分的答案。您可以保留相当大的内存块,然后使用placement-new 就地构造一个元素:en.cppreference.com/w/cpp/language/new
  • 考虑到需求我宁愿推荐std::deque而不是std::vector。也使用emplace 函数而不是push。
  • 关于性能 - 让它工作 - 测量 - 优化。

标签: c++ iterator containers subclass stdvector


【解决方案1】:

这里容器类型和元素存储的选择其实是正交的:即使元素是分开分配的,std::vector&lt;std::unique_ptr&lt;A&gt;&gt;也不能支持高效的删除和迭代同时。您当然可以reset 一个元素,但是每次迭代都必须花时间跳过它。

可以使用空闲列表来扩充数据结构以允许重用空槽,并使用空槽计数以允许在迭代期间有效地跳过它们。结果是“colony” data structure(可能以std::hive 的形式包含在C++23 中)。请注意,它不会追加:新元素以未指定的顺序重复使用已删除元素的空间。

元素类型还有待选择:可以使用std::variant&lt;A,B&gt;,类似的自定义类型知道B派生自A(以便为客户提供A&amp;更容易),或者您可以使用单独的容器方法(对排序有自己的影响)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-06
    • 1970-01-01
    • 1970-01-01
    • 2021-10-01
    相关资源
    最近更新 更多