std :: list是否有一个等效的vector :: reserve()?

时间:2012-05-20 14:32:58

标签: c++ list stl vector

我有一个看起来像这样的课程:

typedef std::list<char*> PtrList;
class Foo
{
public:
   void DoStuff();
private:
   PtrList m_list;
   PtrList::iterator m_it;
};

函数DoStuff()基本上将元素添加到m_list或从中删除元素,找到其中某个特殊元素的迭代器并将其存储在m_it中。请务必注意,m_it的每次调用都会使用DoStuff()的每个值。

那么问题是什么? 一切正常,但分析显示由于new调用list::push_back()而导致运算符DoStuff()被调用太多。

为了提高性能,我想在m_list的初始化中为Foo预先分配内存,就像我std::vector那样。问题是这会引入新问题,例如:

  1. 效率较低的inserterase元素。
  2. 一旦向量从一个调用更改为m_it到另一个调用,
  3. DoStuff()就会变为无效。 编辑: Alan Stokes建议使用索引而不是迭代器来解决此问题。
  4. 我的解决方案:我能想到的最简单的解决方案是实现一个也具有链表功能的对象池。通过这种方式,我得到一个链接列表可以为它预先分配内存。

    我错过了什么或者它是否真的是最简单的解决方案?我宁愿不“重新发明轮子”,而是使用标准解决方案,如果它存在的话。

    任何想法,变通方法或启发性评论都将不胜感激!

5 个答案:

答案 0 :(得分:7)

我认为你错误地使用了容器。

如果你想快速推回,那么不要自动假设你需要一个链表,链表是一个缓慢的容器,它基本上适合重新排序。

更好的容器是std :: deque。 deque基本上是一个数组数组。它会分配一块内存并在你向后推时占用它,当它用完时会分配另一个块。这意味着它只是很少分配,你不必提前知道容器的大小,例如std :: vector和reserver

答案 1 :(得分:4)

您可以使用splice中的std::list功能来实现池。添加新成员变量PtrList m_Pool。如果要添加新对象且池不为空,请将值分配给池中的第一个元素,然后将其拼接到列表中。要擦除元素,请将其从列表中拼接到池中。

但是如果你不关心元素的顺序,那么deque可以快得多。如果要删除中间的元素,请将最后一个元素复制到要删除的元素上,然后删除最后一个元素。

答案 2 :(得分:2)

我的建议与111111相同,尝试在编写任何重要代码之前切换到deque

但是,要直接回答您的问题:您可以将std::list与自定义分配器一起使用。它有点繁琐,我不会在这里完成所有细节,但它的要点是你编写了一个代表列表节点的内存分配策略的类。由list分配的节点将是一个小于char*的小实现定义量,但它们都将具有相同的大小,这意味着您可以为该大小编写一个优化的分配器(池中)内存块而不是对象池,并且您可以向其添加功能,以便您在需要时在分配器中保留所需的任何空间。然后列表可以快速分配/释放。这使您无需重新实现任何实际的list功能。

如果您(由于某种原因)要实现具有列表功能的对象池,那么您可以从boost::intrusive开始。在编写自己的分配器时,这可能也很有用,可以跟踪您的空闲块列表。

答案 3 :(得分:0)

可能会使用list::get_allocator().allocate()。 Afaik,默认行为是由于list s的非连续性而导致获取内存 - 因此缺少reserve() - 但使用allocator方法没有主要缺点我立刻想到了。如果您的计划中有非关键部分,在开始时或其他任何部分,您至少可以选择在此时获得损害。

答案 4 :(得分:0)

列表和向量在管理对象方面完全不同。

Vector将元素构造到给定容量的已分配缓冲区中。当容量耗尽时,会发生新的分配。 List逐个分配元素,每个元素分别放入一个空间。

当插入/移除某些内容时,向量元素会移位,因此,向量索引和元素地址不稳定。 当插入/移除某些内容时,List元素会重新链接,因此,列表迭代器和元素地址是稳定的。

使列表行为与向量类似的方法是替换默认分配器(每次调用时分配系统)与另一个分配较大块中的对象,将子块分配给列表当它调用它时。 这不是标准库默认提供的内容。