减少 stl 向量的容量

Posted

技术标签:

【中文标题】减少 stl 向量的容量【英文标题】:reduce the capacity of an stl vector 【发布时间】:2009-07-10 18:03:48 【问题描述】:

有没有办法减少向量的容量?

我的代码将值插入到向量中(事先不知道它们的编号),并且 完成后,向量仅用于读取操作。

我想我可以创建一个新向量,使用大小和副本执行 .reseve() 物品,但我不太喜欢额外的复制操作。

PS:我不关心便携式解决方案,只要它适用于 gcc。

【问题讨论】:

请注意,reserve() 不一定保留您传递给它的确切金额;它保留的金额大于或等于您传递给 reserve() 的金额。 请注意,交换习语确实会执行复制。我不知道 GCC 是否有一个扩展来释放未使用的保留内存。在我看来,这种方法应该是 vector. 的标准 考虑使用双端队列而不是向量。它几乎和向量一样快,但不将数据保存在连续块中,也不需要reserve() 向量也不需要reserve();这样做更有效,而不是在 push_back()s 期间根据需要不断扩展其长度。 比 reserve() 更具体的问题是,双端队列不需要 O(N) 容量过剩,只需 O(1) 容量过剩。向量在调整自身大小时需要 O(N) 的容量过剩,以实现在末尾插入分摊 O(1) 时间的要求。这就是为什么 deque 是一个很好的建议。 【参考方案1】:
std::vector<T>(v).swap(v);

用另一个向量交换内容会交换容量。

  std::vector<T>(v).swap(v); ==> is equivalent to 

 std::vector<T> tmp(v);    // copy elements into a temporary vector
         v.swap(tmp);              // swap internal vector data

Swap() 只会改变内部数据结构。

【讨论】:

是的 - 这本质上是一个副本。 AFAIK这是做OP想要的唯一标准方法。至于避免复制的非标准方式(OP 认为可以接受),我不确定 GCC 的 STL 是否有这个(但如果有,我不会感到惊讶)。 @avakar - 是的,这保证符合标准(请参阅 aJ 对正在发生的事情的分解)。但它也保证复制原始向量的所有元素。 Michael,你能指出它在哪里指定,在调用向量的复制构造函数后,容量()==大小()?我只是扫了一遍,找不到任何这样的保证。 对,我的观点是原始向量的副本可以预分配与原始向量一样多的内存(即vector(v).capacity() == v.capacity() 始终保持的实现仍然可以兼容)。如果没有capacity() == size() 保证(或至少某种capacity() &lt; size() + k 保证),vector&lt;T&gt;(v).swap(v) 实际上可能根本不会导致收缩。 对于它的价值,这个答案中概述的方法是 Stroustrup 在他的“C++ 编程语言”一书中所说的方法。【参考方案2】:

使用C++11,可以调用成员函数shrink_to_fit()。 draft standard 第 23.2.6.2 节说:

shrink_to_fit 是一个非绑定请求 将capacity() 减少到size()[注意:请求不绑定到 允许纬度 特定于实现的优化。 ——尾注]

【讨论】:

【参考方案3】:

去看看 Scott Meyers Effective STL item 17。

基本上你不能直接减少std::vector 的存储大小。 resize()reseve() 永远不会减少容器的实际内存占用。 “技巧”是创建一个大小合适的新容器,复制数据并将其与当前容器交换。如果我们想清除一个容器,这很简单:

std::vector<T>().swap(v);

如果我们必须复制数据,那么我们需要进行复制:

std::vector<T>(v).swap(v);

这样做是用旧向量中的数据创建一个新向量,执行任何具有您需要的效果的操作所需的副本。然后调用swap() 只会交换对象之间的内部缓冲区。在行尾,创建的临时向量被删除,但它具有旧向量的内脏,旧向量具有我们需要的确切大小的新副本的内脏。

【讨论】:

在 mac os 10.15 和通过 clang / xcode 我使用交换进行了广泛的测试,它从未减小实际大小......【参考方案4】:

惯用的解决方案是用新构造的向量进行交换。

vector<int>().swap(v);

编辑:我误读了这个问题。上面的代码将清除向量。 OP 希望保持元素不变,只将capacity() 缩小到size()

很难说 aJ 的代码是否会这样做。我怀疑是否有便携式解决方案。对于gcc,您必须查看他们对vector 的特定实现。

edit:所以我查看了 libstdc++ 的实现。看来aJ的方案确实可行。

vector<int>(v).swap(v);

参见the source,第 232 行。

【讨论】:

注意 - 这会释放向量的内存 - 但它也会删除 所有 元素(因为您正在使用空的临时向量进行交换)。【参考方案5】:

不,您不能在不复制的情况下减少向量的容量。但是,您可以通过检查 capacity() 并在每次插入内容时调用 reserve() 来控制新分配的增长量。 std::vector 的默认行为是每次需要新容量时将其容量增加 2 倍。你可以按照自己的魔法比例来增长它:

template <typename T>
void myPushBack(std::vector<T>& vec, const T& val) 
    if (vac.size() + 1 == vac.capacity()) 
        vac.reserve(vac.size() * my_magic_ratio);
    

    vec.push_back(val);

如果您喜欢一些 hacky 技术,您可以随时传入自己的分配器并执行您需要做的任何事情来回收未使用的容量。

【讨论】:

【参考方案6】:

我并不是说 GCC 在没有副本的情况下不能有一些方法来做你想做的事,但实现起来会很棘手(我认为)因为向量需要使用 Allocator 对象来分配和解除分配内存,Allocator 的接口不包含reallocate() 方法。我认为这不是不可能的,但可能会很棘手。

【讨论】:

我认为向量容器是不可能的,因为元素需要在内存中是连续的。我还没有意识到分配器只有 .allocate() 和 .deallocate(),所以我想这就是为什么不存在这样的功能的原因。 realloc() 函数在 C 中很简单,但是当您开始向内存分配添加更多语义时,它会变得更加复杂。我认为这就是 C++ 中没有对应物的原因。【参考方案7】:

如果您担心向量的开销,那么也许您应该考虑使用另一种类型的数据结构。您提到,一旦您的代码完成初始化向量,它就会成为一个只读进程。我建议使用开放式数组,它允许程序在编译时决定其容量。或者,链接列表可能更适合您的需求。 如果我完全误解了你的意思,请让我知道。

-UBcse

【讨论】:

如果你只有很少的元素,其他数据结构会有更多的开销。如果有需要数百万个容器的情况。在这种情况下,std::vector 需要的内存要少得多。【参考方案8】:

旧线程,我知道,但万一有人在将来查看它.. C++11 中有shrink_to_fit() 但由于它是一个非绑定请求,因此行为将取决于它的实现。

见:http://en.cppreference.com/w/cpp/container/vector/shrink_to_fit

【讨论】:

【参考方案9】:

我不是 C++ 专家,但似乎 this 解决方案有效(至少用 g++ 编译它):

std::vector<int>some_vector(20);//initial capacity 10
//first you gotta resize the vector;
some_vector.resize(10);
//then you can shrink to fit;
some_vector.shrink_to_fit();
//new capacity is 10;

【讨论】:

【参考方案10】:

这也有效:

Try it online!

v = std::vector<T>(v); // if we need to keep same data
v = std::vector<T>(); // if we need to clear

它调用= 运算符的&amp;&amp; 重载,该运算符执行移动,swap() 使用相同的重载。

【讨论】:

【参考方案11】:

获取 Scott Myers 的“Effective STL”一书。关于减少向量的容量,它有一个完整的项目。

【讨论】:

以上是关于减少 stl 向量的容量的主要内容,如果未能解决你的问题,请参考以下文章

`resize` 是不是有降低向量容量的风险?

STL 向量:移动向量的所有元素

是否需要清除 STL 向量

如何使用 STL 获取结构向量并将其转换为结构元素的向量

动态数组与 STL 向量的确切区别?

基本 STL:向量列表或列表向量,在这种特殊情况下哪个更好?