增加载体容量时的安全问题

Ily*_*lya 0 c++ vector

我偶然发现了一种情况,增加向量的容量会损害与其元素相关的一个变量,我希望有人帮助我理解问题到底是什么。

假设我有一个类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()

fab*_*ian 5

对返回值的任何访问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>>,但这意味着向量只能移动,不能复制。