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,则 &vec[4]
将保持不变,直到向量具有 1000 个元素。之后,我可以调用push_back()
,它会工作,但之前存储的&vec[4]
的指针可能不再有效。
【讨论】:
所以,对于空向量,即vec,保留vec[1]后会以段错误结束。 vec[1] 将是未定义的行为。std::vector::reserve
会阻止在push_back
上偶尔复制整个数组吗?
这仅适用于 C++11 还是特定的 std 实现?看起来保留和使用 [] 访问的代码可以正常工作吗? godbolt.org/z/MhgFdZ
@Steve_Corrin 未定义的行为未定义。它似乎可以工作。它仍然是无效代码。不允许对容器的 < 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 签名?