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

Posted

技术标签:

【中文标题】std::vector::resize() 与 std::vector::reserve()【英文标题】:std::vector::resize() vs. std::vector::reserve() 【发布时间】:2012-10-23 11:19:35 【问题描述】:

this post 的 cmets 部分有一个关于使用 std::vector::reserve()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是对还是错?

【问题讨论】:

解释可能就像“我错了”一样简单:D 我将此标记为“过于本地化”,因为@LuchianGrigore 很少出错 @Default 将“很少出错”读作“快速纠正错误”:) 原帖中的代码已更新为正确使用resize(),疑虑解除。版主:如果这个问题“过于本地化”,请随时删除,如果您认为它将来可能对其他人有帮助,请保留它。 当我将项目从 vc6 迁移到 vs2013 时,这个问题实际上消除了我的疑虑。谢谢 :)) 【参考方案1】:

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

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

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

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

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

【讨论】:

所以,对于空向量,即vec,保留vec[1]后会以段错误结束。 vec[1] 将是未定义的行为。 std::vector::reserve 会阻止在push_back 上偶尔复制整个数组吗? 这仅适用于 C++11 还是特定的 std 实现?看起来保留和使用 [] 访问的代码可以正常工作吗? godbolt.org/z/MhgFdZ @Steve_Corrin 未定义的行为未定义。它似乎可以工作。它仍然是无效代码。不允许对容器的 &lt; size() 中不存在的元素进行索引。根据语言的定义,它们在那里不存在。如果您的编译器决定不启动核弹,而只是按照您希望的方式戳/窥视 RAM,那就祝您好运了。或者运气不好,我猜;理想情况下,我们可以捕获所有程序员都会做的所有无效事情,但是 祝你好运 永远做到这一点!【参考方案2】:

这取决于你想做什么。 reserve添加任何 vector 的元素;它只会改变capacity(),它 保证 adding 元素不会重新分配(例如 使迭代器无效)。 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() 不会。

【讨论】:

调整大小从不释放内存。当大小变小时,会调用析构函数,但会保留内存(容量不会改变)。【参考方案4】:

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

【讨论】:

【参考方案5】:

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

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

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

另一方面,reserve 仅请求将内部缓冲区增长到指定大小,但不会更改数组的“大小”,仅更改其缓冲区大小。

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

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

所以要回答您的问题,是的,您是对的,即使您保留了足够的空间,您仍然使用索引运算符访问未初始化的内存。使用 int 并不是那么糟糕,但在类向量的情况下,您将访问尚未构造的对象。

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

【讨论】:

以上是关于std::vector::resize() 与 std::vector::reserve()的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 C++11 中更改了 std::vector::resize 签名?

如果 std::vector::resize 的 size 参数等于当前大小怎么办?

正则表达式 (\S+?) 与 (\S+)) [重复]

什么是C/S模式与B/S模式,两者区别与优缺点

Java 中的正则表达式,\\s 与 \\s+

C/S与B/S架构 区别·与优点