std :: vector :: resize()与std :: vector :: reserve()


80

这篇文章的评论部分有一个关于使用std::vector::reserve()vs的主题。std::vector::resize()

这是原始代码:

void MyClass::my_method()
{
    my_member.reserve(n_dim);
    for(int k = 0 ; k < n_dim ; k++ )
         my_member[k] = k ;
}

我相信要在中编写元素vector,正确的做法是调用std::vector::resize()而不是std::vector::reserve()

实际上,以下测试代码在VS2010 SP1的调试版本中“崩溃”:

#include <vector>

using namespace std;

int main()
{
    vector<int> v;
    v.reserve(10);
    v[5] = 2;

    return 0;
}

我是对还是错?VS2010 SP1是正确的还是错误的?


11
解释可能很简单,例如“我错了”:D
Luchian Grigore

7
我将其标记为“过于本地化”,因为@LuchianGrigore很少出错
默认值

1
@Default将“非常错误”读为“快速纠正错误” :)
Luchian Grigore 2012年

1
原始帖子中的代码已更新为正确使用resize(),疑惑已清除。主持人:如果此问题“过于本地化”,请随时删除;如果您认为将来可能对其他人有所帮助,请保留此问题。
C64先生2012年

1
当我将项目从vc6迁移到vs2013时,这个问题实际上消除了我的疑问。谢谢:))
pengMiao19

Answers:


112

原因有两种不同的方法:

std::vector::reserve 将分配内存,但不会调整向量的大小,该向量的逻辑大小与以前相同。

std::vector::resize实际上会修改矢量的大小,并会使用默认状态下的对象填充任何空间。如果它们是整数,则全部为零。

保留后,根据您的情况,您将需要大量push_backs写入元素5。如果您不希望这样做,则应使用resize。

关于保留的一件事:如果随后使用push_back添加元素,直到达到保留的容量,向量中任何现有的引用,迭代器或指向数据的指针将保持有效。因此,如果我保留1000,而我的大小为5,&vec[4]则将保持不变,直到向量具有1000个元素。在那之后,我可以调用push_back()它并且它将起作用,但是&vec[4]先前存储的指针可能不再有效。


因此,对于空向量(即vec),保留vec [1]后将以段错误结束。
hailinzeng 2015年

2
vec [1]将是不确定的行为。
CashCow

是否可以std::vector::reserve防止偶尔复制整个阵列push_back
自发布

这仅适用于C ++ 11还是特定的std实现?看起来带有[]保留和访问权限的代码可以正常工作吗?godbolt.org/z/MhgFdZ
Steve_Corrin

1
@Steve_Corrin未定义的行为是未定义的。它似乎可以工作。它仍然是无效的代码。< size()不允许对容器中不存在的元素进行索引。根据语言的定义,它们在那里不存在。如果您的编译器决定不启动核函数,而只是按照您希望的方式戳/偷看RAM,那就太幸运了。我想还是运气不好。理想情况下,我们可以捕获所有程序员都会做的所有无效事情,但是祝您好运
underscore_d

25

Jan Hudec在这里回答:vector :: resize()和vector :: reserve()之间的选择

两种功能的作用截然不同。

resize()方法(传递给构造函数的参数等效于此方法)会将给定数量的元素插入向量(它具有可选的第二个参数来指定其值)。它将影响size(),迭代将遍历所有这些元素,push_back将在它们之后插入,您可以使用operator []直接访问它们。

reserve()方法仅分配内存,但不进行初始化。它仅影响Capacity(),但size()将保持不变。这些对象没有任何价值,因为没有添加任何向量。如果您随后插入元素,则不会发生重新分配,因为它是预先完成的,但这是唯一的效果。

因此,这取决于您想要什么。如果要包含1000个默认项目的数组,请使用resize()。如果您希望向其插入1000个项目的数组,并且希望避免几个分配,请使用reserve()。

编辑:高炉的评论使我再次阅读了该问题并意识到,在您的情况下,正确的答案是不手动进行预分配。只需根据需要在最后插入元素。向量将根据需要自动重新分配,并且比上述手动方式更有效。reserve()有意义的唯一情况是,当您对总大小有合理精确的估计时,您将需要提前轻松获得该估计。

EDIT2:广告问题编辑:如果您有初始估计,则要比那个估计的reserve()大,如果事实证明还不够,只需让向量完成任务即可。


12

这取决于您想做什么。 reserve不会在上添加任何元素vector;它仅更改capacity(),从而保证添加的元素不会重新分配(例如,使迭代器无效)。 resize立即添加元素。如果您想在以后添加元素(insert()push_back()),使用reserve。如果要稍后访问元素(使用[]at()),请使用resize。因此,您MyClass::my_method可以是:

void MyClass::my_method()
{
    my_member.clear();
    my_member.reserve( n_dim );
    for ( int k = 0; k < n_dim; ++ k ) {
        my_member.push_back( k );
    }
}

要么

void MyClass::my_method()
{
    my_member.resize( n_dim );
    for ( int k = 0; k < n_dim; ++ k ) {
        my_member[k] = k;
    }
}

您选择的是一个口味问题,但是您引用的代码显然是错误的。


3

可能应该讨论何时使用小于矢量当前大小的数字调用这两种方法。

reserve()用小于容量的数字呼叫不会影响大小或容量。

resize()以小于当前大小的数字进行调用时,容器将缩小为该大小,从而有效地破坏了多余的元素。

总结起来resize()会释放内存,而reserve()不会。


调整大小永远不会释放内存。当大小变小时,将调用析构函数,但会保留内存(容量不变)。
John Gordon

2

是的,您是对的,Lucian刚刚打错了字,可能太缺乏咖啡了,无法意识到自己的错误。


1

调整大小实际上会更改向量中的元素数量,如果调整大小导致向量增大,则会默认构造新项。

vector<int> v;
v.resize(10);
auto size = v.size();

在这种情况下,大小为10。

另一方面,reserve保留只要求将内部缓冲区增大到指定的大小,而不更改数组的“大小”,仅更改其缓冲区大小。

vector<int> v;
v.reserve(10);
auto size = v.size();

在这种情况下,大小仍为0。

因此,要回答您的问题,是的,您是对的,即使您保留了足够的空间,您仍在使用索引运算符访问未初始化的内存。用int表示还不错,但是在使用类向量的情况下,您将访问尚未构造的对象。

显然,这种行为可能会混淆设置为调试模式的编译器的边界检查,这可能就是您遇到崩溃的原因。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.