c++ - How do I delete an object pointer from a vector without causing a memory error? -
i have vector of object pointers adding , deleting while looping through update objects. can't seem delete objects have "died" vector without causing memory error. i'm not sure i'm doing wrong. listed below update method , 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 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 pan 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
member variable,vector<object*> objects;
p
member variable,vector<object*>::iterator p;
so p
iterator, *p
object pointer, , **p
object.
the problem method:
void engine::killupdate(std::string command) { if ((*p)->getisdead() == true) { delete *p; } }
deallocates object pointed *p
, pointer in vector @ position referenced p
iterator. pointer *p
still in vector, points memory no longer allocated. next time try use pointer, cause undefined behavior , crash.
so need remove pointer vector once have deleted object points to. could simple as:
void engine::killupdate(std::string command) { if ((*p)->getisdead() == true) { delete *p; objects.erase(p); } }
however, calling killupdate
update
in loop iterates on objects
vector. if use code above, have problem: once erase p
objects
vector, no longer safe execute p++
in for-loop statement, because p
no longer valid iterator.
fortunately, stl provides nice way around this. vector::erase
returns next valid iterator after 1 erased! can have killupdate
method update p
instead of for-loop statement, e.g.
void engine::update(string command) { if (getgameover() == false) { (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 of course assuming always call killupdate
in loop, i'm sure can see way around if don't -- execute p++
@ end of for-loop body in case haven't called killupdate
.
also note this not particularly efficient, since every time erase element of vector, elements follow have shifted fill in empty space. slow if objects
vector large. if used std::list
instead (or if using that), not problem, lists have other drawbacks.
a secondary approach overwrite each pointer deleted object nullptr
, use std::remove_if
remove them in 1 go @ end of loop. e.g.:
void engine::update(string command) { if (getgameover() == false) { (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 time never have nullptr
element of objects
want keep reason.
since seem beginner, should note this:
std::erase(std::remove_if(objects.begin(), objects.end(), [](const object* o) { return o == nullptr; }), objects.end());
is the erase-remove idiom, explained on wikipedia. erases elements vector if return true when given function object called on them. in case, function object is:
[](const object* o) { return o == nullptr; }
which lambda expression , shorthand instance of object type:
class isnull { public: bool operator() (const object* o) const { return o == nullptr; } };
one last caveat second approach, noticed have another loop on objects
in scrollupdate
. if choose second approach, sure update loop check nullptr
s in objects
, skip them.
Comments
Post a Comment