Vector erase function deleting wrong object - c++

I have a vector declared as:
vector<Curve> workingSet;
Curve is a class I've created that contains a string "name" and an array of structs, dynamically allocated by the constructor.
I have a loop that is supposed to delete 2 (out of 4) items from the vector.
vector<Curve>::iterator it = workingSet.begin();
//remove tbi's children from the working set
for ( ; it != workingSet.end();){
if (it->thisName == tbi.childAName ||it->thisName == tbi.childBName)
it= workingSet.erase(it);
else
++it;
}
When the debugger reaches the .erase(it) call I can see that the 'it' iterator is pointing to curve 2 in the vector. This is good; I want curve 2 to be removed from the vector.
I'm then taken by the debugger to the destructor (I have a breakpoint there), which presumably should be destructing curve 2. But when I look at the 'this' watch, I can see that the curve being destructed is curve 4! The destructor then proceeds to 'delete[]' the array in the object, as required and sets the array pointer to NULL.
When the debugger returns to the program, having completed the erase() call, I can see that vector 2 has been removed from the array and curve 4 is still there. Curve 4's array pointer still points to the same location as before, but the memory has been deallocated and the array contains garbage.
Can anyone suggest why curve 4 is being messed with?
N.b. (1) There is a copy constructor for the curve class, which does a 'deep' copy.
N.b. (2) There is more to the class/program than I've mentioned here
Btw, the array pointers in curves 2 and 4 point to different locations and contain different values, according to the debugger.
Edit: I've now implemented copy assignment. Now the correct item seems to be being erased from the vector, but the wrong destructor is still being called! However, when the debugger returns to the array curve 4 is still intact.

When an item is erased from the vector, all elements behind it are shifted towards the front to fill the empty space. If your compiler doesn't support move yet, it's done by copying all the elements, and the last item in the vector, which is now copied to the item before it, is a duplicate and is getting deleted.
At least that's how it should work.

It appears to me that vector::erase cannot be used with a vector of locally constructed non-trivial data types (objects not constructed on the heap, the proper name escapes me right now). I have the exact same behavior you describe, the last element gets destructed twice (especially dangerous if your object has memory that is freed by the destructor) and the element that you removed is never destructed. I do not know why this happens, but it is a pitfall to watch out for.
Here is one way to fix the problem:
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class MyClass
{
public:
int *x;
MyClass(int x)
{
cout << "MyClass Constructor " << x << endl;
this->x = new int(x);
}
MyClass(const MyClass& obj)
{
this->x = new int(*obj.x);
cout << "copy constructor " << *this->x << endl;
}
~MyClass()
{
cout << "MyClass Destructor " << *x << endl;
delete x;
}
};
int main(int argc, char* argv[])
{
// incorrect
vector<MyClass> bad_vect;
for(int i=0;i<3;i++){
bad_vect.push_back(MyClass(i));
// causes a bunch of copying to happen.
// std::move does not seem to fix this either
// but in the end everything gets constructed as we'd like
}
cout << " ---- " << endl;
bad_vect.erase(bad_vect.begin() + 1); // we expect this to remove item with x = 1 and destruct it.
// but it does NOT do that, it does remove the item with x=1 from the vector
// but it destructs the last item in the vector, with x=2, clearly
// not what we want. The destructor for object with x=1 never gets called
// and the destructor for the last item gets called twice.
// The first time with x=2 and since that memory is freed, the 2nd time
// it prints garbage. Strangely the double-free doesn't crash the prog
// but I wouldn't count on that all the time.
// Seems some parts of STL have pitfalls with locally constructed objects
cout << " ------------ " << endl;
// below fixes this
vector<unique_ptr<MyClass> >vect;
for(int i=0;i<3;i++){
unique_ptr<MyClass> ptr(new MyClass(i));
vect.push_back( move( ptr )); // move is required since unique_ptr can only have one owner
// or the single one-liner below
//vect.push_back( move( unique_ptr<MyClass>(new MyClass(i)) ));
}
// the above prints out MyClass Constructor 0,1,2, etc
vect.erase(vect.begin() + 1); // remove the 2nd element, ie with x=1
// the above prints out MyClass Destructor 1, which is what we want
for(auto& v : vect){
cout << *(v->x) << endl;
} // prints out 0 and 2
return 0; // since we're using smart pointers, the destructors for the
// remaining 2 objects are called. You could use regular pointers
// but you would have to manually delete everything. shared_ptr
// also works and you don't need the move(), but with a bit more overhead
}

Related

How do vector elements preserve their original address after a vector std::move?

As you can see in the output, the objects of the vector pre not only "moved" to the vector post, but also preserved their original address space in memory. What is really going on behind this move? Is this behaviour expected? Say I need to have a separate vector of pointers to these objects, is it safe to assume that after this move the objects will always have their original addresses?
Actually, I have a class containing a vector like this and the vector of pointers I mentioned as members. I have also deleted the copy ctors, and defined the move ones for the class.
#include <iostream>
#include <vector>
struct B {
int val = 0;
B(int aInt) : val(aInt) { };
};
int main() {
std::vector<B> pre;
pre.push_back(B(1));
pre.push_back(B(2));
std::cout << "pre-move:\t" << (void*)&pre.at(0) << '\n';
std::cout << "pre-move:\t" << (void*)&pre.at(1) << '\n';
std::vector<B> post(std::move(pre));
std::cout << "post-move:\t" << (void*)&post.at(0) << '\n';
std::cout << "post-move:\t" << (void*)&post.at(1) << '\n';
return 0;
}
Output:
pre-move: 0x1d7b150
pre-move: 0x1d7b154 <------|
post-move: 0x1d7b150 |
post-move: 0x1d7b154 <------|
A vector is basically nothing more than a pointer to heap-allocated memory, the current length and the current capacity of the vector.
By "moving" a vector, all you're doing is copying those values, and resetting the values of the moved-from vector.
For the data of the vector, it's basically equivalent to
original_pointer = some_place_in_memory;
new_pointer = original_pointer; // Copies the *value* of original_pointer
original_pointer = nullptr;
There's no need to allocate new memory and copy the data in the vector.
The whole point of the move operation is to avoid copying the elements, so if they got copied(there is no such thing as truly "moving" the memory) the move would be just a copy.
Vectors are usually implemented as 3 pointers: begin,end and capacity. All point to a dynamically-allocated array. Then moving the vector is just copying those three pointers and so the array and elements just change their owner.
I think it should be safe to assume that pointers to the elements remain valid.
It will be clear, if we write semantically equal code without std::vector:
B* pre = new B[2]; // Declare std::vector<B> and allocate some space to make the following line correct
B[0] = 1; // pre.push_back(B(1));
B[1] = 2; // pre.push_back(B(2));
B* post = pre; // std::vector<B> post(std::move(pre));
Actually, vector move boils down to pointer copying without reallocation. Data which the pointer points at remains in it's place, so addresses of vector elements do not change.
In this code example after the fourth line, both pre and post point to the same data with same address.
std::vector is a wrapper for a pointer to array with some additional functionality. So after doing std::vector<B> post(std::move(pre));, post will contain a pointer with the same value which was in pre.

Does a vector re-create all object on a push_back()?

I noticed that whenever I push_back() an object into a vector, all existing objects in the vector seem to be "re-created".
To test this I made the following test program down below. What the program does is to push_back() a few instances of MyClass into a vector and print the adresses of the current objects in the vector after each push_back(). The constructor and destructor prints a message with the adress of the object whenever an instance is created/destroyed.
#include "stdafx.h"
#include <vector>
#include <iostream>
using namespace std;
class MyClass {
public:
MyClass() {
cout << "Constructor: " << this << endl;
}
~MyClass() {
cout << "Destructor: " << this << endl;
}
};
void printAdresses(const vector<MyClass> & v) {
for (int i = 0; i < v.size(); i++) {
cout << &v[i] << endl;
}
}
int main()
{
vector<MyClass> v;
for (int i = 0; i < 4; i++) {
v.push_back(MyClass());
cout << "Contains objects: " << endl;
printAdresses(v);
cout << endl;
}
system("pause");
return 0;
}
Down below is the output I got from running the program. Every block is the output from one iteration of the loop.
Constructor: 00EFF6AF
Destructor: 00EFF6AF
Contains objects:
034425A8
Constructor: 00EFF6AF
Destructor: 034425A8
Destructor: 00EFF6AF
Contains objects:
034426F8
034426F9
Constructor: 00EFF6AF
Destructor: 034426F8
Destructor: 034426F9
Destructor: 00EFF6AF
Contains objects:
034424E8
034424E9
034424EA
Constructor: 00EFF6AF
Destructor: 034424E8
Destructor: 034424E9
Destructor: 034424EA
Destructor: 00EFF6AF
Contains objects:
034423F8
034423F9
034423FA
034423FB
Appart from the constructor and destructor of the item that was given as argument for the push_back(), destructors of other objects are called during a push_back() (but no corresponding constructors?). When taking a closer look at the adresses we see that the destructors belong to elements in the vector prior to the push_back(). Afterwards the vector contains other objects with different adresses, but the same number of elements (plus the newly push_backed).
What all this tells me is that all objects in the vector are destroyed and re-created after each push_back. If this is the case, why is that? It seems really inefficient when a vector gets too large. If it's not the case, then what exactly happens?
Vector pre-allocates some space.
For example, it can have an initial size of 2 elements. When you try to push the 3rd one, it re-allocates for 4 elements. When you try to push the 5th element, it re-allocates for 8 elements and so on.
That's why you have a .size() that reflects how many elements you have and a .capacity() that reflects what is the capacity (how many elements can be stored before a reallocation is needed).
You can manually handle that capacity by calling .reserve()
To answer your original question, reallocation requires copying all the elements from the previous array to a bigger one.
This requires to destroy the previous elements and to create some new ones.
If you saw the destructors calls but not the matching constructor calls, this is because it does not use the default constructor, but the copy constructor:
MyClass(const MyClass&)
This constructor takes one object has a parameter and uses it to construct and initialize a new object.
Moreover, as mentioned in the comments: even though the re-allocation/copy process can be seen as expensive, on average it is not and actually constitutes a constant time operation. This is because the cost of re-allocation and copy is amortized and future insertion because we do not re-allocate the exact space needed, but more space than necessary.

Iterate through vector of objects c++

I've been struggling with this problem so I hope someone can help me.
I am creating a vector of objects and I would like to access elements of this vector but when I push back en element into the vector and try to access it, it doesn't work.
Thank you for your answers.
class reserve{
private:
vector<float> coordinates;
public:
reserve(const reserve &);
void addACoordinate(float);
void readCoordinates();
int getDim();
};
reserve::reserve(const reserve &w){
int i;
for(i=0; i<coordinates.size(); i++){
coordinates[i] = w.coordinates[i];
}
}
void reserve::addACoordinate(float c){
coordinates.push_back(c);
}
void reserve::readCoordinates(){
int i;
cout << "the coordinates are : ";
for(i=0; i<coordinates.size(); i++){
cout << coordinates[i] << " ";
}
cout << endl;
}
int reserve::getDim(){
return coordinates.size();
}
Then I create a reserve with 2 coordinates and push it into the reserves vector.
vector<reserve> reserves;
reserve r;
r.addACoordinate(1.9);
r.addACoordinate(1.9);
r.getDim();
reserves.push_back(r);
cout << "size of reserves " << reserves.size() << endl;
for (vector<reserve>::iterator k = reserves.begin(); k != reserves.end(); ++k) {
k->getDim();
}
But the output of the iteration is 0, instead of 2. I don't understand why I am not accessing the reserve r.
The shown copy constructor is completely broken:
reserve::reserve(const reserve &w){
int i;
for(i=0; i<coordinates.size(); i++){
The newly-constructed coordinates class member is always completely empty when the copy-constructor runs. It obviously won't magically have any content right from the start, so coordinates.size() will always be zero here. The copy constructor will never actually copy the element that's being used to copy from, and the alleged "copy" of it will always have an empty vector. And changing this to w.coordinates.size() will result in memory corruption, unless you also replace the assignment in the loop with a push_back(). Speaking of push_back()...
reserves.push_back(r);
push_back() attempts to copy r into the vector, here. That's it's job. Guess what's going to be used to do that? Why, the broken copy constructor, of course. Even if r has an initialized coordinates vector, the broken copy constructor will ensure that reserves will end up with a class instance with a completely empty coordinates vector.
You can completely get rid of the copy constructor, here. It serves no useful purpose, and the default copy constructor will do everything correctly.
If you need to manually implement a copy constructor for your homework assignment, fix it so it properly push_back()s the values from the class instance being copied from, or manually copies the class members itself, whatever you need to do the job.

Adding vector to 2D vector, and keeping reference to the last vector

I am writing a program and there was a very subtle error that was hard to recognize.
I examined the program for hours and it looks like it's from my misuse of resize() in allocating a new vector in 2D vector, from my misuse of back() in vector, or from updating a reference variable.
I wrote a simpler code that contains similar problem in my original program for demonstration:
int main() {
vector<vector<int>> A(1);
vector<int> &recent = A.back();
recent.emplace_back(50);
cout << A.back()[0] << endl; //prints 50
A.resize(2);
A[1] = vector<int>();
cout << A.size() << endl; //prints 2
recent = A.back();
cout << A[1].size() << endl; //prints 0
recent.emplace_back(20); //Segmentation fault occurs!!
cout << A[1][0] << endl;
}
Segmentation fault occurs when I tried to emplace_back(20), although in my original program it doesn't throw any error and doesn't emplace the element either.
Possible cause of problem, in my opinion is:
I used resize() to allocate a new vector after the current last position of the 2D vector A, because I didn't know how to emplace_back() an empty vector.
2, 3. In recent = A.back();, I'm not sure if I am updating the reference variable(defined as vector<int> &recent) correctly, and if back() gives the correct reference to the newly allocated vector at the end of the 2D vector A.
The logic looked perfectly fine, but obviously I am doing something wrong.
What am I doing wrong, and what can I do instead?
References in C++ cannot be "updated". The call to resize may (and likely will) invalidate any reference to the original content of the vector. Thus recent is a dangling reference after A.resize(2);.
When creating the initial A here
std::vector<std::vector<int>> A(1);
the outer vector is required to be able to store one single vector.
If you add another std::vector<int> to A the first element of A is likely to move to another memory location. Since recent will always refer to the old memory location you see the segfault.
See 'c++ Vector, what happens whenever it expands/reallocate on stack?' for how vectors work.
On the question how to circumvent this:
If you know the size of the vector in advance you could use reserve to prevent the vector A from reallocating its contents. You'd nevertheless face the problem that references cannot be "reassigned". You can always use A.back() to refer to the last element.
You can use a function taking a reference argument which will be bound upon calling the function:
void do_stuff(std::vector<int> & recent)
{
// do stuff with recent
}
std::vector<std::vector<int>> A;
while (condition)
{
// add whatever to A
A.emplace_back(std::vector<int>{});
// do stuff with last element
do_stuff(A.back());
}
Another way to do it is with scope:
std::vector<std::vector<int>> A(1);
{
std::vector<int> &recent = A.back();
recent.emplace_back(50);
std::cout << A.back()[0] << std::endl; //prints 50
A.resize(2);
} // recent goes out of scope here
std::cout << A.size() << std::endl; //prints 2
{
std::vector<int> &recent = A.back(); // another recent indepedant of first one
std::cout << A[1].size() << std::endl; //prints 0
recent.emplace_back(20);
std::cout << A[1][0] << std::endl; // prints 20
}
Let's step through the code line by line.
vector<vector<int>> A(1);
vector<int> &recent = A.back();
Here we create a vector with one default-constructed vector<int> as its contents. We then bind a reference to the last and only element.
recent.emplace_back(50);
cout << A.back()[0] << endl; //prints 50
We now emplace 50 into the sole vector and print it.
A.resize(2);
Now we resize the vector. If space needs to be reallocated, all iterators, pointers and references to the contents are now invalid.
A[1] = vector<int>();
cout << A.size() << endl; //prints 2
This is fine, as there is enough space in A.
recent = A.back();
BANG
This assignment doesn't rebind recent, it tries to assign A.back() to the referencee. If space was reallocated for A, recent is no longer a valid reference, so we run off into the realm of undefined behaviour.
Quite honestly, using A.back() directly rather than maintaining a reference to it is probably your best bet. If you absolutely want to hold some kind of reference to the end, this is a reasonable use of a non-owning pointer.
From the discussion in the comments, it appears that your original problem was:
vector<vector<int>> very_long_name_that_cannot_be_changed;
and that you want a shorthand notation to access the last element of this:
auto& short_name = very_long_name_that_cannot_be_changed;
short_name.resize(100); // will expand the vector, but not change the reference
short_name.back().emplace_back(20); // presto, quick accesss to the last element.
This is proof against resizing, because the reference just tracks the vector, not its last element.

vector of object all the same object after adding separate objects to it

So I'm adding a bunch of objects to a vector inside of my class. I am able to add everything, or it seems, just fine, but when I go to print the contents of the vector everything is the very last object that I added. Why is that? This is what it looks like when I add
this->packages.push_back(mc);
This is what it looks like when I call the object I want to print
std::cout << truck.packages[1]->getTrack() << endl;
I'm not sure what I'm doing wrong, vectors seem pretty straight forward on adding and removing items.
If I need to add anything else, please let me know.
std::cout << truck.packages[1]->getTrack() << endl;
std::cout << truck.packages[2]->getTrack() << endl;
Output: 11111
11111
This is how I create the object
if(type == 0){
Letter *letter = new Letter();
letter->setType("Letter");
letter->setWeight(weight);
letter->setTrack(track);
price = letter->cost();
letter->setPrice(price);
this->packages.push_back(letter);
truckWeight = this->getGross() + (weight / 16);
this->setGross(truckWeight);
this->numPackages++;
delete letter;
This is letter.cpp
Letter::Letter()
{
this->type = "Letter";
this->price = 0.0;
this->weight = 0;
this->trackingNumber = 0;
}
double Letter::cost()
{
int weight = this->getWeight();
if(weight > 32)
return 0.0;
else
return (0.5 * weight);
}
This is letter.h
class Letter: public Package{`enter code here`
public:
Letter();
double cost();
};
Made it with Letter. It doesn't matter which object I use. They all do the same thing
You have UB in your code. You have a vector of Letter*s. When you push_back these Letter*s, the vector just makes a copy of these pointers. Once you call delete on the original Letter*, these vector elements are now dangling. You can handle this in two ways :
Make package a vector<Letter> instead of vector<Letter*>. Now, you just happily push_back(or, preferable, emplace_back) Letters.
If you must use pointers, use unique_ptr(or some other smart pointer). So, package would be vector<unique_ptr>. When the vector gets destroyed, the unique_ptrs will take care of memory deallocation.
If you don't want to use a smart pointer, you will have to manage the deletes yourself. So, if package is a member of MyAwesomePackage(no pun intended), you would do this in the destructor:
MyAwesomePackage::~MyAwesomePackage()
{
///Other destruction
for(auto * letter : package)
delete letter;
}
While you don't show the declaration of the vector, your usage in this statement:
std::cout << truck.packages[1]->getTrack() << endl;
shows that you're not building a vector of objects, but a vector of pointers to objects.
If the vector contains only a pointer, and you modify the object referenced by this pointer after pushing the pointer into the vector, you will see the new value when you access it later through the pointer in the vector.
You are probably doing something like this:
std::vector<Package*> packages;
mc = new Package;
mc->setFoo(1);
packages.push_back(mc);
mc->setFoo(2);
packages.push_back(mc);
You now have two pointers to the same object in the vector. Since you modified the object after the first push_back() call, you will see the new value of the object when you access either element in the vector.
To make this work, you will need to either store actual objects in the vector:
std::vector<Package> packages;
or allocate a new Package object each time before you push the pointer into the vector.
After seeing the full code, your problem is a slight variation of this: You're deleting the object after pushing a pointer to it into the vector. So the vector contains pointers to objects that have been deleted, which leads to undefined behavior. What is probably happening in your test case is that when you allocate a new object after deleting the previous one, you're getting a pointer to the same memory again. That's why it looks like all values are the same. But even that is "random" and completely unspecified behavior.