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 nullptrs in objects , skip them.


Comments

Popular posts from this blog

python - mat is not a numerical tuple : openCV error -

c# - MSAA finds controls UI Automation doesn't -

wordpress - .htaccess: RewriteRule: bad flag delimiters -