添加到 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 在该过程中保持不变。
我尝试过的其他事情:
- 如果我去掉在
set()
里面加Node
的那一行,那么每次都会输出X = 3。
- 如果我创建一个新的
Node
并在其上调用它 (Node p = nodes[0]
),则输出为 3, 3, 3
- 如果我创建一个引用
Node
并在其上调用它 (Node &p = nodes[0]
),则输出为 3, 0, 0(这可能是因为当向量调整大小时引用丢失?)
这是出于某种原因的未定义行为吗?为什么?
您的代码有未定义的行为。在
void set(){
X = 3;
cout << "Before, X = " << X << endl;
nodes.push_back(Node());
cout << "After, X = " << X << endl;
}
对X
的访问实际上是this->X
而this
是指向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
进行编译,甚至会告诉您在哪一行释放了内存。
我在以下情况下发现了一些非常奇怪的行为(在 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 在该过程中保持不变。
我尝试过的其他事情:
- 如果我去掉在
set()
里面加Node
的那一行,那么每次都会输出X = 3。 - 如果我创建一个新的
Node
并在其上调用它 (Node p = nodes[0]
),则输出为 3, 3, 3 - 如果我创建一个引用
Node
并在其上调用它 (Node &p = nodes[0]
),则输出为 3, 0, 0(这可能是因为当向量调整大小时引用丢失?)
这是出于某种原因的未定义行为吗?为什么?
您的代码有未定义的行为。在
void set(){
X = 3;
cout << "Before, X = " << X << endl;
nodes.push_back(Node());
cout << "After, X = " << X << endl;
}
对X
的访问实际上是this->X
而this
是指向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
进行编译,甚至会告诉您在哪一行释放了内存。