我偶然发现了一种情况,增加向量的容量会损害与其元素相关的一个变量,我希望有人帮助我理解问题到底是什么。
假设我有一个类MyObject和一个vector<MyObject> myVector已经填充了 4 个元素的容器。我也有一个方法:
MyObject* GetFirstActiveElement(vector<MyObject> vec)
{
for (auto& val : vec)
{
if (val->IsActive())
return &val;
}
return nullptr;
}
Run Code Online (Sandbox Code Playgroud)
然后我有一段代码如下:
MyObject myObject new MyObject();
MyObject* firstActiveElement = GetFirstActiveElement(myVector);
myVector.insert(myVector.begin() + 1, myObject);
Run Code Online (Sandbox Code Playgroud)
在最后一行之后,如果我检查firstActiveElement,如果不是nullptr有时它现在是垃圾。
阅读一些文档后,我发现既然myVector有 4 个元素,并且其默认容量为 4,那么再插入一个元素会导致其容量以静默方式增加,而这个 C++ 文档说:
如果new_cap大于capacity(),则所有迭代器(包括尾后迭代器)以及对元素的所有引用都将无效。否则,迭代器或引用不会失效。
我实际上认为这firstActiveElement只是一个指针,所以无论如何它都不应该失效。但显然,它恰好是一个迭代器或对向量的引用,是真的吗?我在这里有点迷失,但我想原因是我对方法的设计GetFirstActiveElement()。
对返回值的任何访问GetFirstActiveElement始终是未定义的行为,因为向量是按值传递给函数的,在函数内部,您正在处理存储MyObject在调用函数内的向量中的 s 的副本;返回时这些副本会被销毁。
即使您传递调整向量大小的引用,也可能会导致向量元素的地址发生变化(或者更确切地说,通过移动旧对象在新后备存储中构造不同的对象。
以下示例演示了这一点:
int main() {
std::vector<int> v;
v.push_back(1);
void* p1 = &v[0];
v.reserve(1000);
void* p2 = &v[0];
std::cout << "p1=" << p1 << "\np2=" << p2 << '\n';
}
Run Code Online (Sandbox Code Playgroud)
可能的输出:
int main() {
std::vector<int> v;
v.push_back(1);
void* p1 = &v[0];
v.reserve(1000);
void* p2 = &v[0];
std::cout << "p1=" << p1 << "\np2=" << p2 << '\n';
}
Run Code Online (Sandbox Code Playgroud)
如果你想保持MyObjects 的地址稳定,你可以使用 a std::vector<std::unique_ptr<MyObject>>,但这意味着向量只能移动,不能复制。