VS2010 中的 vector::insert 执行意外结果
Posted
技术标签:
【中文标题】VS2010 中的 vector::insert 执行意外结果【英文标题】:vector::insert in VS2010 performs unexpected result 【发布时间】:2013-06-06 15:31:08 【问题描述】:我偶尔在 VS2010 的下一个代码中发现了一个奇怪的问题:
void Test1()
std::vector<int> vec;
vec.push_back(10);
vec.push_back(20);
vec.insert(vec.end(), vec[0]);
// GCC: vec == [10, 20, 10];
// VS2005: vec == [10, 20, 10];
// VS2010: vec == [10, 20, -17891602];
似乎向量在读取新值之前会重新分配内存并删除旧内存,这会导致复制损坏的值。 VS2010 中存在此问题。签入 VS2005 和 GCC - OK。
将来自 operator[] 或 front()/back() 方法的引用传递给 insert() 是否有效?
UPD:基于下面的 cmets 做了一些调查,我得出的结论是,由于性能原因,使用 reserve() 并不是一个好主意。这会导致不必要的大量重新分配。
void Test2()
std::vector<int> vec, vec2;
const int count = 10000;
int prevCap = 0, reallocCount = 0;
int prevCap2 = 0, reallocCount2 = 0;
for (int i = 0; i < count; ++i)
if (vec.size() >= vec.capacity())
vec.reserve(vec.size()+1);
vec.insert(vec.end(), i);
vec2.insert(vec2.end(), i);
const int cap = vec.capacity();
const int cap2 = vec2.capacity();
if (prevCap != cap) ++reallocCount;
prevCap = cap;
if (prevCap2 != cap2) ++reallocCount2;
prevCap2 = cap2;
cout << reallocCount << " " << reallocCount2 << endl;
// reallocCount == 10000, reallocCount2 == 15 GCC
所以现在我只有两个选择:
1) 使用临时变量
const int tempValue = vec[0];
vec.insert(vec.end(), tempValue);
但我不确定编译器是否可以通过一些优化来删除 tempValue。
2) 使用 push_back(0) 和更多 pop_back() 调用
vec.push_back(0);
vec.pop_back();
vec.insert(vec.end(), vec[0]);
这种方法似乎更好,它在 VS2005/2010 和 GCC 中提供了预期的结果和性能。
我错过了什么吗?有没有更好的解决方案?
【问题讨论】:
drdobbs.com/cpp/copying-container-elements-from-the-c-li/… 【参考方案1】:insert 通过引用获取第二个参数。 op[] 也提供参考。 调用 insert 会使引用无效,因此您有未定义的行为,任何事情都可能发生。
【讨论】:
【参考方案2】:insert 的文档说(我的重点)
通过在元素之前插入新元素来扩展向量 指定位置,有效地增加容器大小 插入的元素数。
这会导致分配的存储空间自动重新分配 如果 - 且仅当 - 新向量大小超过当前向量 容量。
由于insert
的第二个参数是一个引用,并且重新分配可能需要重新定位您的堆单元,这表明您的代码只有在您知道重新分配不会发生时才是安全的。即当vec.capacity() > vec.size()
正如 Agnew 和 quetzalcoatl 所指出的,有几种方法可以修复您的代码
您可以将新值复制到一个临时值,以确保对其的引用仍然有效
int val = vec[0];
vec.insert(vec.end(), val);
...或者您可以在调用insert
之前增加向量的容量
if (vec.capacity == vec.size())
vec.reserve(vec.size()+1);
vec.insert(vec.end(), val);
【讨论】:
当然,如果vec[0]
返回的引用在insert
开始之前被转换为临时引用,这不会有问题,但事实并非如此。
因此,在这样的使用之间,应该使用vector::reserve 以确保在插入期间不会发生重新分配:vec.reserve(vec.size()+1); vec.insert(vec.end(), vec[0]);
始终是安全的。好吧,至少在 size+1 超出内存之前 ;)
@Angew, quetzalcoatl 感谢 cmets,我已经相应地更新了我的答案以上是关于VS2010 中的 vector::insert 执行意外结果的主要内容,如果未能解决你的问题,请参考以下文章
VS2008 中的 C++ 项目有效,但在 VS2010 中无效