添加到 std::vector 时 class 字段的奇怪行为

Weird behaviour with class fields when adding to a std::vector

我在以下情况下发现了一些非常奇怪的行为(在 clang 和 GCC 上)。我有一个向量,nodes,有一个元素,class Node 的一个实例。 然后,我在 nodes[0] 上调用一个函数,向向量添加一个新的 Node。 添加新节点时,将重置调用对象的字段!但是,一旦函数完成,它们似乎 return 又恢复正常。

我相信这是一个最小的可重现示例:

#include <iostream>
#include <vector>

using namespace std;

struct Node;
vector<Node> nodes;

struct Node{
    int X;
    void set(){
        X = 3;
        cout << "Before, X = " << X << endl;
        nodes.push_back(Node());
        cout << "After, X = " << X << endl;
    }
};

int main() {
    nodes = vector<Node>();
    nodes.push_back(Node());

    nodes[0].set();
    cout << "Finally, X = " << nodes[0].X << endl;
}

输出

Before, X = 3
After, X = 0
Finally, X = 3

尽管您希望 X 在该过程中保持不变。

我尝试过的其他事情:

这是出于某种原因的未定义行为吗?为什么?

您的代码有未定义的行为。在

void set(){
    X = 3;
    cout << "Before, X = " << X << endl;
    nodes.push_back(Node());
    cout << "After, X = " << X << endl;
}

X的访问实际上是this->Xthis是指向vector成员的指针。当您执行 nodes.push_back(Node()); 时,您向向量添加了一个新元素并且该过程重新分配,这 使向量中元素的所有迭代器、指针和引用 无效。这意味着

cout << "After, X = " << X << endl;

正在使用不再有效的 this

nodes.push_back(Node());

将重新分配向量,从而更改 nodes[0] 的地址,但 this 不会更新。
尝试用此代码替换 set 方法:

    void set(){
        X = 3;
        cout << "Before, X = " << X << endl;
        cout << "Before, this = " << this << endl;
        cout << "Before, &nodes[0] = " << &nodes[0] << endl;
        nodes.push_back(Node());
        cout << "After, X = " << X << endl;
        cout << "After, this = " << this << endl;
        cout << "After, &nodes[0] = " << &nodes[0] << endl;
    }

注意调用 push_back&nodes[0] 有何不同。

-fsanitize=address 会捕捉到这一点,如果您还使用 -g 进行编译,甚至会告诉您在哪一行释放了内存。