Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 inside set(), 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?

like image 963
Qq0 Avatar asked Mar 02 '20 19:03

Qq0


People also ask

Does std::vector Push_back make a copy?

Yes, std::vector<T>::push_back() creates a copy of the argument and stores it in the vector.

Should I use std for vector?

As a rule of thumb, you should use: a std::array if the size in fixed at compile time. a std::vector is the size is not fixed at compile time. a pointer on the address of their first element is you need low level access.

How do you check if something is present in a std::vector?

You can use the find function, found in the std namespace, ie std::find . You pass the std::find function the begin and end iterator from the vector you want to search, along with the element you're looking for and compare the resulting iterator to the end of the vector to see if they match or not.

Is std::vector on the stack?

Although std::vector can be used as a dynamic array, it can also be used as a stack.


1 Answers

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.

like image 78
NathanOliver Avatar answered Sep 21 '22 06:09

NathanOliver