In my code a have a global vector of Node object and a local vector of Node pointers:
#include<cstdio>
#include<cstdlib>
#include<vector>
using namespace std;
class Node {
int n;
public:
Node(int i) : n(i);
int getN() { return n; }
};
vector<Node> v;
int main() {
vector<Node*> p;
v.push_back(Node(1));
p.push_back(&v[0]);
printf("first node id : %d\n", (*p[0]).getN());
return 0;
}
I inserted a node object to global vector & inserted the pointer of that object in the local vector. Output of my above code is:
first node id : 1
However, if I change my main function to this:
int main()
{
vector<Node*> p;
v.push_back(Node(1));
p.push_back(&v[0]);
v.push_back(Node(2));
p.push_back(&v[1]);
printf("first node id : %d\n", (*p[0]).getN());
return 0;
}
The code prints a garbage value:
first node id : 32390176
I can't figure out the problem.
Does the vector
data structure changes the references of each object after an insertion ?
How can I fix this ?
Yes, std::vector<T>::push_back() creates a copy of the argument and stores it in the vector.
C++ Vector Library - push_back() Function The C++ function std::vector::push_back() inserts new element at the end of vector and increases size of vector by one.
It does not have any return value.
The reason is that vector provides an insert method that does return an iterator to the inserted position. The standard is consistent in that push_back doesn't return a value but some forms of insert will, when available and appropriate.
Yes, a push_back()
on a vector invalidates all references (and pointers) to elements in that vector if it has to reallocate. There are various ways to work around this. If you know that your vector will have a particular number of nodes, you can use reserve()
. In your example, you could reserve two elements:
int main()
{
v.reserve(2);
.
.
.
}
This will make sure the vector has preallocated enough storage so that it doesn't need to reallocate.
If you don't know the size ahead of time, then you'll have to change something about your approach. You might use a std::deque
instead of a std::vector
, since std::deque
doesn't invalidate references when you use push_back()
. You might store indices instead of pointers. Or you might need to push all the nodes into your vector before making the pointers.
int main()
{
v.push_back(Node(1));
v.push_back(Node(2));
vector<Node*> p;
p.push_back(&v[0]);
p.push_back(&v[1]);
printf("first node id : %d\n", (*p[0]).getN());
return 0;
}
Possibly, yes. An std::vector
may reallocate its (heap) storage when you add/push_back()
additional elements, invalidating all pointers:
Iterator [read: Pointer] Invalidation
(for operations)
push_back
,emplace_back
... If the vector changed capacity, all of them [i.e. all iterators are invalidated]. If not, onlyend()
.
The above invalidation rule does not apply if a vector's capacity does not change due to an insertion - since vectors do not reallocate storage unnecessarily. So if you pre-set your vector's capacity to 2 in your example (say, with v.reserve(2)
), the pointer will remain valid. If you don't know the size in advance, but you can delay the construction of the second vector (with the pointers), you don't have to reserve, you'll just have the size after inserting the last element.
The approaches above are highly unrecommended, however. If you were to make your vector constant - at least in the scope of a function in which you would construct and use the second vector - you would have a strong guarantee of non-reallocation. Alternatively, if you could determine the size in advance you might use an std::array
, and it would be more fitting to use pointers into that container's storage:
Iterator Invalidation
As a rule, iterators to an array are never invalidated throughout the lifetime of the array.
You might also consider storing indices into your vector (although there, as well, the vector might shrink, invalidating the indices, or you might insert elements in the middle etc).
Anyway, I suspect that you might not actually want to do any of this, i.e. it seems to be a not-so-good solution to a problem which could be handled with a different approach altogether.
PS - If the vector has a custom allocator then everything I've written might be irrelevant.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With