我偶尔会在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];
}
看起来,vector会在读取新值之前重新分配内存并删除旧内存,从而导致复制损坏的值。此问题出现在VS2010中。在VS2005和GCC中检查 - 确定。
传递给operator()从operator []或front()/ back()方法获取的引用是否有效?
UPD:根据以下评论做了一些调查,我得出结论,因为性能,使用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中给出了预期的结果和性能。
我错过了什么吗?有更好的解决方案吗?
答案 0 :(得分:4)
insert通过引用获取第二个参数。 op []也提供了参考。 调用insert会使引用无效,因此您有未定义的行为,任何事情都可能发生。
答案 1 :(得分:3)
insert的文档说(我的重点)
通过在元素之前插入新元素来扩展向量 指定的位置,有效地增加了容器的大小 插入的元素数量。
这会导致自动重新分配已分配的存储空间 if - 并且仅当 - 新的矢量大小超过当前矢量 容量。强>
由于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);