如何在std :: vector中管理动态内存?

时间:2009-03-23 05:59:10

标签: c++ memory-management stl vector

std :: vector如何实现对不断变化的元素数量的管理:它是使用realloc()函数还是使用链表?

感谢。

4 个答案:

答案 0 :(得分:11)

它使用赋予它的分配器作为第二个模板参数。就像这样。假设它在push_back中,让t成为要推送的对象:

...
if(_size == _capacity) { // size is never greater than capacity
    // reallocate
    T * _begin1 = alloc.allocate(_capacity * 2, 0);
    size_type _capacity1 = _capacity * 2;

    // copy construct items (copy over from old location).
    for(size_type i=0; i<_size; i++)
        alloc.construct(_begin1 + i, *(_begin + i));
    alloc.construct(_begin1 + _size, t);

    // destruct old ones. dtors are not allowed to throw here. 
    // if they do, behavior is undefined (17.4.3.6/2)
    for(size_type i=0;i<_size; i++)
        alloc.destroy(_begin + i);
    alloc.deallocate(_begin, _capacity);

    // set new stuff, after everything worked out nicely
    _begin = _begin1;
    _capacity = _capacity1;
} else { // size less than capacity
    // tell the allocator to allocate an object at the right
    // memory place previously allocated
    alloc.construct(_begin + _size, t);
}
_size++; // now, we have one more item in us
...

这样的事情。分配器将关心分配内存。它保留了分配内存和将对象构建到该内存中的步骤,因此它可以预分配内存,但尚未调用构造函数。在重新分配期间,向量必须注意由复制构造函数抛出的异常,这会使问题复杂化。以上只是一些伪代码片段 - 不是真正的代码,可能包含许多错误。如果大小超过容量,它会要求分配器分配一个新的更大的内存块,如果没有,那么它只是在先前分配的空间构建。

这个的确切语义取决于分配器。如果它是标准分配器,则构造将执行

new ((void*)(_start + n)) T(t); // known as "placement new"

分配allocate只会从::operator new获得内存。 destroy会调用析构函数

(_start + n)->~T();

所有在分配器后面抽象的东西,矢量只是使用它。堆栈或池分配器可以完全不同。关于vector重要的一些要点

  • 在致电reserve(N)后,您最多可以将N个项目插入到您的向量中,而不会有重新分配的风险。在此之前,只要size() <= capacity(),它的元素的引用和迭代器仍然有效。
  • Vector的存储是连续的。您可以将&amp; v [0]视为包含当前向量中元素的缓冲区。

答案 1 :(得分:7)

向量的硬性规则之一是数据将存储在一个连续的内存块中。

这样你就知道你理论上可以做到这一点:

const Widget* pWidgetArrayBegin = &(vecWidget[0]);

然后,您可以将 pWidgetArrayBegin 传递给希望将数组作为参数的函数。

唯一的例外是std :: vector&lt; bool&gt;专业化。它实际上根本不是bool,但这是另一个故事。

因此std :: vector将重新分配内存,并且不会使用链接列表。

这意味着你可以通过以下方式射击自己:

Widget* pInteresting = &(vecWidget.back());
vecWidget.push_back(anotherWidget);

如你所知,push_back调用可能导致向量将其内容转移到一个全新的内存块,使 pInteresting 无效。

答案 2 :(得分:1)

std::vector管理的内存保证是连续的,这样您就可以将&vec[0]视为指向动态数组开头的指针。

鉴于此,它如何实际管理它的重新分配是特定于实现的。

答案 3 :(得分:1)

std :: vector将数据存储在连续的内存块中。

假设我们将向量声明为

std :: vector intvect;

所以最初会创建一个x元素的内存。这里x是实现依赖的。

如果用户插入的元素多于新元素,则将创建2x(两倍大小)元素,并将初始向量复制到该内存块中。

这就是为什么总是建议通过调用reserve来为vector保留内存 功能

intvect.reserve(100);

以避免删除和复制矢量数据。