Strange problem with vectors - c++

I have a really strange problem with stl vectors in which the wrong destructor is called for the right object when I call the erase method if that makes any sense.
My code looks something like this:
for(vector<Category>::iterator iter = this->children.begin(); iter != this->children.end(); iter++)
{
if((*iter).item == item)
{
this->children.erase(iter);
return;
}
-------------------------
}
It's just a simple function that finds the element in the vector which has some item to be searched, and removes said element from the vector. My problem is than when the erase function is called, and thus the object which the iterator is pointing at is being destroyed, the wrong destructor is being called. More specific the destructor of the last element in the vector is being called, and not of the actual object being removed. Thus the memory is being removed from the wrong object, which will still be an element in the vector, and the actual object which is removed from the vector, still has all of it's memory intact.
The costructor of the object looks like this:
Category::Category(const Category &from)
{
this->name = from.name;
for(vector<Category>::const_iterator iter = from.children.begin(); iter != from.children.end(); iter++)
this->children.push_back((*iter));
this->item = new QTreeWidgetItem;
}
And the destructor
Category::~Category()
{
this->children.clear();
if(this->item != NULL)
{
QTreeWidgetItem* parent = this->item->parent();
if(parent != NULL) parent->removeChild(this->item);
delete this->item;
}
}

When you erase your element from the vector, each element after it is copied (using the assignment operator) to the previous spot in the vector. Once this is complete, the last element in the vector is destructed. This could be why you're seeing your last element get destructed. Rule number one when using the STL is to ensure the copy semantics for your object are correct.
You should consider writing an assignment operator:
Category & operator =(const Category & other);
Although this may not be as simple as it sounds, considering objects will be copied and destructed many times in the vector.

You probably should use standard algorithms.
The main issue I see is that your destructor for Category asks its parent vector to remove it. That cannot be right, the destructor happens only when the vector is already removing it.
std::vector uses placement-new so will be calling your destructor directly. I do not know what the effect will be to go back to the vector at this point.
Remove the line if (parent != NULL ) parent->removeChild(this->item) from the destructor. It is not what you want.

This is expected behavior. On my implementation (and I am guessing, on yours) when erasing an element from a vector, elements from n+1 to the end are assigned into it and the very last element is destructed.
Use std::list if you don't want this to happen.
Demo:
#include <iostream>
#include <vector>
struct Category
{
int item;
Category(int n=0) : item(n) {}
~Category() { std::cout << "Category " << item << " destroyed\n"; }
};
int main()
{
std::vector<Category> children(3);
children[0] = Category(0);
children[1] = Category(1);
children[2] = Category(2);
int item = 0;
std::cout << " beginning the loop \n";
for( std::vector<Category>::iterator iter = children.begin();
iter != children.end(); ++iter)
{
if(iter->item == item)
{
children.erase(iter); // prints "Category 2 destroyed"!
break;
}
}
std::cout << " loop done \n";
} // this will print "Category 1 destroyed" and "Category 2 destroyed"
And yes, explicit erase/remove_if is more readable than the loop.

Related

How do I delete an object pointer from a vector without causing a memory error?

I have a vector of object pointers that I am adding to and deleting from while looping through to update objects. I can't seem to delete objects that have "died" from the vector without causing a memory error. I'm not really sure what I'm doing wrong. Listed below is my update method and it's sub method.
void Engine::update(string command){
if(getGameOver()==false){
for(p=objects.begin();p!=objects.end();p++){
spawnUpdate(command);
//cout<<"Spawn"<<endl;
objectUpdate(command);
//cout<<"object"<<endl;
scrollUpdate(command);
// cout<<"scroll"<<endl;
killUpdate(command);
//cout<<"kill"<<endl;
}
}
}
void Engine::killUpdate(std::string command){
if((*p)->getIsDead()==true){delete *p;}
}
void Engine::objectUpdate(string command){
(*p)->update(command,getNumObjects(),getObjects());
if(((*p)->getType() == PLAYER)&&((*p)->getPosX()>=getFinishLine())){setGameOver(true);}
}
void Engine::scrollUpdate(string command){
//Check player position relative to finishLine
if(((*p)->getType() == PLAYER)&&((*p)->getPosX()>(SCREEN_WIDTH/2))){
(*p)->setPosX((*p)->getPosX()-RUN_SPEED);
setFinishLine(getFinishLine()-RUN_SPEED);
for(q=objects.begin();q!=objects.end();q++){
//Shift objects to pan the screen
if((*q)->getType() == OPPONENT){(*q)->setPosX((*q)->getPosX()-RUN_SPEED);}
if((*q)->getType() == BLOCK){(*q)->setPosX((*q)->getPosX()-RUN_SPEED);}
}
}
}
void Engine::spawnUpdate(string command){
if(command.compare("shoot")==0){
cout<<"Bang!"<<endl;
if((*p)->getType() == PLAYER){objects.push_back(new Bullet((*p)->getPosX(),(*p)->getPosY(),(*p)->getState()));cout<<"Bullet success "<<endl;}
}
}
Some assumptions/definitions:
objects a member variable, something like vector<Object*> objects;
p is also a member variable, something like vector<Object*>::iterator p;
So p is an iterator, *p is an Object pointer, and **p is an Object.
The problem is that this method:
void Engine::killUpdate(std::string command) {
if ((*p)->getIsDead() == true) {
delete *p;
}
}
deallocates the Object pointed to by *p, the pointer in the vector at the position referenced by the p iterator. However the pointer *p itself is still in the vector, now it just points to memory that is no longer allocated. Next time you try to use this pointer, you will cause undefined behavior and very likely crash.
So you need to remove this pointer from your vector once you have deleted the object that it points to. This could be as simple as:
void Engine::killUpdate(std::string command) {
if ((*p)->getIsDead() == true) {
delete *p;
objects.erase(p);
}
}
However, you are calling killUpdate from update in a loop that iterates over the objects vector. If you use the code above, you will have another problem: once you erase p from the objects vector, it is no longer safe to execute p++ in your for-loop statement, because p is no longer a valid iterator.
Fortunately, STL provides a very nice way around this. vector::erase returns the next valid iterator after the one you erased! So you can have the killUpdate method update p instead of your for-loop statement, e.g.
void Engine::update(string command) {
if (getGameOver() == false) {
for (p = objects.begin(); p != objects.end(); /* NOTHING HERE */) {
// ...
killUpdate(command);
}
}
}
void Engine::killUpdate(std::string command) {
if ((*p)->getIsDead() == true) {
delete *p;
p = objects.erase(p);
} else {
p++;
}
}
This is of course assuming that you always call killUpdate in the loop, but I'm sure you can see the way around this if you don't -- just execute p++ at the end of the for-loop body in the case that you haven't called killUpdate.
Also note that this is not particularly efficient, since every time you erase an element of the vector, the elements that follow it have to be shifted back to fill in the empty space. So this will be slow if your objects vector is large. If you used a std::list instead (or if you are already using that), then this is not a problem, but lists have other drawbacks.
A secondary approach is to overwrite each pointer to a deleted object with nullptr and then use std::remove_if to remove them all in one go at the end of the loop. E.g.:
void Engine::update(string command) {
if (getGameOver() == false) {
for (p = objects.begin(); p != objects.end(); p++) {
// ...
killUpdate(command);
}
}
std::erase(std::remove_if(objects.begin(), objects.end(),
[](const Object* o) { return o == nullptr; }),
objects.end());
}
void Engine::killUpdate(std::string command) {
if ((*p)->getIsDead() == true) {
delete *p;
*p = nullptr;
}
}
The assumption this time is that you will never have a nullptr element of objects that you want to keep for some reason.
Since you seem to be a beginner, I should note that this:
std::erase(std::remove_if(objects.begin(), objects.end(),
[](const Object* o) { return o == nullptr; }),
objects.end());
is the erase-remove idiom, which is explained well on Wikipedia. It erases elements from the vector if they return true when a given function object is called on them. In this case, the function object is:
[](const Object* o) { return o == nullptr; }
Which is a lambda expression and is essentially shorthand for an instance of an object with this type:
class IsNull {
public:
bool operator() (const Object* o) const {
return o == nullptr;
}
};
One last caveat to the second approach, I just noticed that you have another loop over objects in scrollUpdate. If you choose the second approach, be sure to update this loop to check for nullptrs in objects and skip them.
Here is an issue (formatted for readability):
void Engine::update(string command)
{
if (getGameOver()==false)
{
for (p=objects.begin();p!=objects.end();p++)
{
spawnUpdate(command); // changes vector
//...
}
}
//...
}
void Engine::spawnUpdate(string command)
{
//...
objects.push_back(new Bullet((*p)->getPosX(),(*p)->getPosY(),(*p)->getState())); // no
//...
}
You have a loop with iterator p that points to elements in the object vector. When you call objects.push_back, the iterator for the vector may become invalidated. Thus that loop iterator p is no longer any good. Incrementing it in the for() will cause undefined behavior.
One way to get around this is to create a temporary vector that holds your updates. Then you add the updates at the end of your processing:
void Engine::update(string command)
{
std::vector<Object*> subVector;
if (getGameOver()==false)
{
for (p=objects.begin();p!=objects.end();p++)
{
spawnUpdate(command, subVector);
//...
}
}
// add elements to the vector
object.insert(object.end(), subVector.begin(), subVector.end());
}
void Engine::spawnUpdate(string command, std::vector<Object*>& subV)
{
if (command.compare("shoot")==0)
{
cout<<"Bang!"<<endl;
if ((*p)->getType() == PLAYER)
subV.push_back(new Bullet((*p)->getPosX(),(*p)->getPosY(),(*p)->getState()));
cout<<"Bullet success "<<endl;
}
}
You could avoid most of these issues by not using raw pointers. Clearly your code uses the semantic that the vector owns the pointers, so you can express this directly:
std::vector< std::unique_ptr<Object> > objects;
Then you may insert into the vector by using objects.emplace_back(arguments,to,Object,constructor); , and when you remove from the vector it will automatically delete the Object.
You still need to watch out for erase invalidating iterators, so keep using the erase-remove idiom as explained by Tyler McHenry. For example:
objects.erase( std::remove_if( begin(objects), end(objects),
[](auto&& o) { return o->getIsDead(); }), end(objects) );
Note - auto&& is permitted here since C++14; in C++11 you'd have to use std::unique_ptr<Object>&. Required includes are <algorithm> and <memory>.
And please stop using global iterators, keep p local to the function and pass any arguments you need to pass.

Removing a unique_ptr from a list, by its value's pointer

Given a pointer to an object, I am trying to remove the same object from a list of unique_ptrs. I do this by matching each element of a raw pointer list subset of a larger unique_ptr list which definitely contains all of the elements from the subset list. Code:
Edit: For clarity, rawListSubset is std::list<MyObj>
and smartListSet is std::list< unique_ptr<MyObj> >.
for (auto& deleteThis : rawListSubset)
{
// Find the matching unique_ptr
for (auto& smartPtrElement : smartListSet)
{
if (deleteThis == smartPtrElement.get())
{
unique_ptr<Entity> local = move(smartPtrElement);
// Hence deleting the object when out of scope of this conditional
}
}
}
Alternatively, this doesn't work, but it puts across the idea of what I'm trying to do.
for (auto& deleteThis : rawListSubset)
{
// Find the matching unique_ptr
for (auto& smartPtrElement : smartListSet)
{
if (deleteThis == smartPtrElement.get())
{
smartListSet.remove(smartPtrElement);
// After this, the API tries to erroneously copy the unique_ptr
}
}
}
How can I both delete the object that the pointer is pointing to, as well as safely remove it from its list?
To safely remove elements from a std::list in a loop, you have to use iterators. std::list::erase() removes an element specified by an iterator, and returns an iterator to the next element in the list:
for (auto& deleteThis : rawListSubset)
{
// Find the matching unique_ptr
auto i = smartListSet.begin();
auto e = smartListSet.end();
while (i != e)
{
if (deleteThis == i->get())
i = smartListSet.erase(i);
else
++i;
}
}
You can delete elements by iterating over list by using iterators, rather than for range loop:
for (auto& deleteThis : rawListSubset)
{
// Find the matching unique_ptr
for (auto smartPtrIter = smartListSet.begin(); smartPtrIter != smartListSet.end(); )
{
if (deleteThis == smartPtrIter->get())
{
smartListSet.erase(smartPtrIter++);
} else
++smartPtrIter;
}
}
Object pointed by smart pointer will be deleted when you remove that element from the list, that what smart pointers are used for.

c++ when creating new object: iterator not incrementable

I am dealing with a task to create a troop of bunnies where they can multiply at each round. So I define a class of Bunny (individual), and then define a class of Troop with a vector point to different bunnies.
My problem is, every time I use new to create an object Bunny in a loop, it will come out an error says:
"Debug assertion failed!!...vector iterator not incrementable..."
Here is a sample of my code:
class Bunny {
private:
string sex;
string color;
string name;
int age;
public:
string getname() { return name;};
Bunny(); // constructor
};
class Troop {
private:
vector<Bunny *> bunpointer;
vector<Bunny *>::iterator it;
public:
void newbunny();
void multiply();
};
void Troop::newbunny() {
Bunny * bun; // pointer to the Bunny class
bun = new Bunny;
cout << "Bunny " << bun->getname() << " is born! \n";
bunpointer.push_back(bun);
}
void Troop::multiply() {
it = bunpointer.begin();
while(it!=bunpointer.end()) {
cout << (*it)->getname() << " gave a birth. ";
newbunny();
++it;
}
it = bunpointer.begin();
}
So if I create 5 bunnies at the beginning, and call function Troop::multiply, there should be 10 bunnies. An interesting observation is, the error will occur after 2 bunnies being born.
I think the problem may lie in the use of new to create new objects in a iterator loop. The new may somehow interrupt the iterator pointer *it. But I am not sure if this is the case, and if it really is, how to deal with this.
modified: so it is actually a problem of using push_back(), which will probably invalidate the iterator!!
Thank you in advance!!
1) Unless you have a reason to, your code does not need to use new at all. The code becomes easier, and no chance of memory leaks. Also, I don't see the need for an iterator member in the Troop class, unless you can justify the reason for it.
2) As to your immediate problem, just use a non-iterator reliant loop. In other words, a simple loop that goes from 0 to the number of current bunnies, less 1.
Here is an example:
#include <vector>
//...
class Troop {
private:
std::vector<Bunny> bunpointer;
public:
void newbunny();
void multiply();
};
void Troop::newbunny() {
bunpointer.push_back(Bunny());
}
void Troop::multiply() {
size_t siz = bunpoiner.size();
for (size_t i = 0; i < siz; ++i ) {
newbunny();
cout << (*it)->getname() << " gave a birth. ";
}
}
The newbunny() function simply creates a Bunny() using a default constructor and adds the item to the vector.
If you want to use a container that doesn't invalidate iterators when inserting items, then you can use a std::list as opposed to a std::vector.
It appears that you are trying to modify the std::vector while iterating over it. This is generally not a good idea, for a variety of reasons.
In particular, when you call newbunny() inside the iterator's loop, it is possible that the iterator you used to hold will be invalidated, because the vector may be resized during the push_back.
See this question for details.
As mentioned by others, the push_back() is your culprit, not the new.
One way to palliate would be this:
size_t const max(bunpointer.size());
for(size_t i(0); i < max; ++i)
{
cout << bunpointer[i]->getname() << " gave a birth. ";
newbunny();
}
This works because you are only adding new bunnies at the end of your existing vector. These new bunnies are not taken in account by the loop (max doesn't change because you call newbunny()...) and the [i] access makes use of the vector in its current state.
That loop would not work if you were deleting items...
As a side note: the name "bunpointer" is not very clear... it's not a pointer, it's a vector of pointers.

Iterator over a list pointing to wrong elements

I have 2 variables: std::list lst; and std::list<A*>::iterator lstiter with A being a class. The lst is filled with pointers to the class! B is an other class that holds both variables.
Puts iterator at the begin of the list:
void B::iterstart() {
lstiter = lst.begin();
}
void B::iternext() {
iteratend();
lstiter = ++lstiter;
}
void B::iterprev() {
iteratstart();
lstiter = --lstiter;
}
void B::iteratend() {
if (lst.empty())
throw -1;
if (lstiter == lst.end())
throw "Error: end of list\n";
}
void B::iteratstart() {
if (lst.empty())
throw -1;
if (lstiter == lst.begin())
throw "Error: begin of list\n";
}
(I also have a function that gets the pointer at the element in the list the iterator is pointing too at the moment. Iteratend and iteratstart throw an exception when there are no elements in the list and when I try to go past the last or first element. This is where my problem is!
Now I call: iterstart(); iternext(); iternext(); iternext(); I never get the message!(End of list)
And I do have some other bugs too, Sometimes I call the procedure prev, but I get the return value of the procedure next! (But I want to try to solve the other problem first)
This lstiter = ++lstiter is wrong. With an integer it might work but when used with complicated C++ objects it does not always perform correctly because it depends on the specific implementation of the increment and copy functions. Also, why would you want to do it? All you need is ++lstiter.

C++: Why is the destructor being called here?

I guess I don't fully understand how destructors work in C++. Here is the sample program I wrote to recreate the issue:
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
struct Odp
{
int id;
Odp(int id)
{
this->id = id;
}
~Odp()
{
cout << "Destructing Odp " << id << endl;
}
};
typedef vector<shared_ptr<Odp>> OdpVec;
bool findOdpWithID(int id, shared_ptr<Odp> shpoutOdp, OdpVec& vec)
{
shpoutOdp.reset();
for (OdpVec::iterator iter = vec.begin(); iter < vec.end(); iter++)
{
Odp& odp = *(iter->get());
if (odp.id == id)
{
shpoutOdp.reset(iter->get());
return true;
}
}
return false;
}
int main()
{
OdpVec vec;
vec.push_back(shared_ptr<Odp>(new Odp(0)));
vec.push_back(shared_ptr<Odp>(new Odp(1)));
vec.push_back(shared_ptr<Odp>(new Odp(2)));
shared_ptr<Odp> shOdp;
bool found = findOdpWithID(0, shOdp, vec);
found = findOdpWithID(1, shOdp, vec);
}
Just before main() concludes, the output of this program is:
Destructing Odp 0
Destructing Odp 1
Why does this happen? I'm retaining a reference to each of the Odp instances within the vector. Does it have something to do with passing a shared_ptr by reference?
UPDATE I thought that shared_ptr::reset decremented the ref count, based on MSDN:
The operators all decrement the
reference count for the resource
currently owned by *this
but perhaps I'm misunderstanding it?
UPDATE 2: Looks like this version of findOdpWithID() doesn't cause the destructor to be called:
bool findOdpWithID(int id, shared_ptr<Odp> shpoutOdp, OdpVec& vec)
{
for (OdpVec::iterator iter = vec.begin(); iter < vec.end(); iter++)
{
Odp& odp = *(iter->get());
if (odp.id == id)
{
shpoutOdp = *iter;
return true;
}
}
return false;
}
This line right here is probably what is tripping you up.
shpoutOdp.reset(iter->get());
What you're doing here is getting (through get()) the naked pointer from the smart pointer, which won't have any reference tracking information on it, then telling shpoutOdp to reset itself to point at the naked pointer. When shpoutOdp gets destructed, it's not aware that there is another shared_ptr that points to the same thing, and shpoutOdp proceeds to destroy the thing it's pointed to.
You should just do
shpoutOdp = *iter;
which will maintain the reference count properly. As an aside, reset() does decrement the reference counter (and only destroys if the count hits 0).
So many things that are being used nearly correctly:
bool findOdpWithID(int id, shared_ptr<Odp> shpoutOdp, OdpVec& vec)
Here the parameter shpoutOdp is a a copy of the input parameter. Not such a big deal considering it is a shared pointer but that is probably not what you were intending. You probably wanted to pass by reference otherwise why pass it to the function in the first place.
shpoutOdp.reset();
Resetting a parameter as it is passed in.
Does this mean it could be dirty (then why have it as an input parameter) it make the function return a shared pointer as a result if you want to pass something out.
Odp& odp = *(iter->get());
Don't use get on shared pointers unless you really need to (and you really if ever need too). Extracting the pointer is not necessary to get at what the pointer points at and makes you more likely to make mistakes because you are handling pointers. The equivalent safe(r) line is:
Odp& odp = *(*iter); // The first * gets a reference to the shared pointer.
// The second star gets a reference to what the shared
//pointer is pointing at
This is where it all goes wrong:
shpoutOdp.reset(iter->get());
You are creating a new shared pointer from a pointer. Unfortunately the pointer is already being managed by another shared pointer. So now you have two shared pointers that think they own the pointer and are going to delete it when they go out of scope (the first one goes out of scope at the end of the function as it is a copy of the input parameter (rather than a reference)). The correct thing to do is just to do an assignment. Then the shared pointers know they are sharing a pointer:
shpoutOdp = *iter; // * converts the iterator into a shared pointer reference
The next line though not totally wrong does assume that the iterators used are random access (which is true for vector).
for (OdpVec::iterator iter = vec.begin(); iter < vec.end(); iter++)
But this makes the code more brittle as a simple change in the typedef OdpVec will break the code without any warning. So to make this more consistent with normal iterator usage, use != when checking against end() and also prefer the pre increment operator:
for (OdpVec::iterator iter = vec.begin(); iter != vec.end(); ++iter)
shared_ptr::reset destroys the contents already in the shared_ptr. If you want to affect only that single shared_ptr reference, simply assign to it.
EDIT: In response to comment, you can fix it by changing the body of your for loop to:
if ((*iter)->id == id)
{
shpoutOdp = *iter;
return true;
}
EDIT2: That all said, why aren't you using std::find_if here?
#include <iostream>
#include <memory>
#include <vector>
#include <algorithm> //for std::find_if
#include <functional> //for std::bind
struct Odp
{
int id;
int GetId()
{
return id;
}
Odp(int id)
{
this->id = id;
}
~Odp()
{
std::cout << "Destructing Odp " << id << std::endl;
}
};
typedef std::vector<shared_ptr<Odp> > OdpVec;
int main()
{
OdpVec vec;
vec.push_back(std::shared_ptr<Odp>(new Odp(0)));
vec.push_back(std::shared_ptr<Odp>(new Odp(1)));
vec.push_back(std::shared_ptr<Odp>(new Odp(2)));
OdpVec::iterator foundOdp = std::find_if(vec.begin(), vec.end(),
std::bind(std::equal_to<int>(), 0, std::bind(&Odp::GetId,_1)));
bool found = foundOdp != vec.end();
}
The nice thing about shared_ptr is that it handles the ref-counting internally. You don't need to manually increment or decrement it ever. (And that is why shared_ptr doesn't allow you to do so either)
When you call reset, it simply sets the current shared_ptr to point to another object (or null). That means that there is now one less reference to the object it pointed to before the reset, so in that sense, the ref counter has been decremented. But it is not a function you should call to decrement the ref counter.
You don't ever need to do that. Just let the shared_ptr go out of scope, and it takes care of decrementing the reference count.
It's an example of RAII in action.
The resource you need to manage (in this case the object pointed to by the shared_ptr) is bound to a stack-allocated object (the shared_ptr itself), so that its lifetime is managed automatically. The shared_ptr's destructor ensures that the pointed-to object is released when appropriate.