89

这篇文章的评论部分有一个关于使用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 是对的,还是错的?

4

5 回答 5

128

有两种不同的方法是有原因的:

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

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

保留后,在您的情况下,您将需要大量 push_backs 来写入元素 5。如果您不想这样做,那么在您的情况下,您应该使用调整大小。

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

于 2012-10-23T11:24:03.213 回答
15

这取决于你想做什么。 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;
    }
}

您选择哪一个是品味问题,但您引用的代码显然不正确。

于 2012-10-23T11:28:55.990 回答
3

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

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

resize()使用小于当前大小的数字调用容器将减小到该大小,从而有效地破坏多余的元素。

总而言之,resize()将释放内存,而reserve()不会。

于 2015-07-07T21:32:37.820 回答
2

是的,你是对的,Luchian 只是打错了字,可能是因为喝咖啡太少而没有意识到自己的错误。

于 2012-10-23T11:22:35.760 回答
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 并不是那么糟糕,但在类向量的情况下,您将访问尚未构造的对象。

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

于 2012-10-23T11:33:51.163 回答