问题
I have found some very weird behaviour (on clang and GCC) in the following situation. I have a vector, nodes
, with one element, an instance of class Node
.
I then call a function on nodes[0]
that adds a new Node
to the vector.
When the new Node is added, the calling object's fields are reset! However, they seem to return to normal again once the function has finished.
I believe this is a minimal reproducible example:
#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;
}
Which outputs
Before, X = 3
After, X = 0
Finally, X = 3
Though you would expect X to remain unchanged by the process.
Other things I have tried:
- If I remove the line that adds a
Node
insideset()
, then it outputs X = 3 every time. - If I create a new
Node
and call it on that (Node p = nodes[0]
) then the output is 3, 3, 3 - If I create a reference
Node
and call it on that (Node &p = nodes[0]
) then the output is 3, 0, 0 (perhaps this one is because the reference is lost when the vector resizes?)
Is this undefined behaviour for some reason? Why?
回答1:
Your code has undefined behavior. In
void set(){
X = 3;
cout << "Before, X = " << X << endl;
nodes.push_back(Node());
cout << "After, X = " << X << endl;
}
The access to X
is really this->X
and this
is a pointer to the member of the vector. When you do nodes.push_back(Node());
you add a new element to the vector and that process reallocates, which invalidates all iterators, pointers and references to elements in the vector. That means
cout << "After, X = " << X << endl;
is using a this
that is no longer valid.
回答2:
nodes.push_back(Node());
will reallocate the vector, thus changing the address of nodes[0]
, but this
is not updated.
try replacing the set
method with this code:
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;
}
note how &nodes[0]
is different after calling push_back
.
-fsanitize=address
will catch this, and even tell you on which line the memory was freed if you also compile with -g
.
来源:https://stackoverflow.com/questions/60495211/weird-behaviour-with-class-fields-when-adding-to-a-stdvector