Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/141.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何从向量中删除对象指针而不导致内存错误?_C++_Pointers_Object_Vector_Delete Operator - Fatal编程技术网

C++ 如何从向量中删除对象指针而不导致内存错误?

C++ 如何从向量中删除对象指针而不导致内存错误?,c++,pointers,object,vector,delete-operator,C++,Pointers,Object,Vector,Delete Operator,我有一个对象指针向量,在循环更新对象的过程中添加和删除这些指针。我似乎无法删除向量中“死亡”的对象,而不会导致内存错误。我真的不确定我做错了什么。下面列出的是我的更新方法及其子方法 void Engine::update(string command){ if(getGameOver()==false){ for(p=objects.begin();p!=objects.end();p++){ spawnUpdate(command); /

我有一个对象指针向量,在循环更新对象的过程中添加和删除这些指针。我似乎无法删除向量中“死亡”的对象,而不会导致内存错误。我真的不确定我做错了什么。下面列出的是我的更新方法及其子方法

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;}
        }
}
void引擎::更新(字符串命令){
如果(getGameOver()==false){
对于(p=objects.begin();p!=objects.end();p++){
更新(命令);
//coutgetPosX()-运行速度);}
}
}
}
无效引擎::生成更新(字符串命令){
if(command.compare(“shoot”)==0){

coutgetState());cout一些假设/定义:

  • 对象
    成员变量,类似于
    向量对象;
  • p
    也是一个成员变量,类似于
    vector::iterator p;
因此,
p
是一个迭代器,
*p
是一个对象指针,
**p
是一个对象

问题在于,这种方法:

void Engine::killUpdate(std::string command) {
  if ((*p)->getIsDead() == true) {
    delete *p;
  }
}
解除分配由
*p
指向的对象,即向量中由
p
迭代器引用的位置处的指针。但是指针
*p
本身仍在向量中,现在它只指向不再分配的内存。下次尝试使用此指针时,将导致未定义的行为和非常严重的错误可能会崩溃

因此,一旦删除了指针指向的对象,就需要从向量中删除该指针。这可以很简单:

void Engine::killUpdate(std::string command) {
  if ((*p)->getIsDead() == true) {
    delete *p;
    objects.erase(p);
  }
}
但是,您在一个循环中从
update
调用
killUpdate
,该循环迭代
对象
向量。如果使用上述代码,您将遇到另一个问题:一旦您从
对象
向量中删除
p
,则在for循环中执行
p++/code>将不再安全声明,因为
p
不再是有效的迭代器

幸运的是,STL提供了一个很好的解决方法。
vector::erase
返回删除的迭代器之后的下一个有效迭代器!因此您可以使用
killUpdate
方法更新
p
而不是for循环语句,例如

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++;
  }
}
当然,这是假设您总是在循环中调用
killUpdate
,但我相信如果您不这样做,您可以看到解决方法——在没有调用
killUpdate
的情况下,只需在for循环体的末尾执行
p++

还请注意,这不是特别有效的,因为每次删除向量的元素时,后面的元素都必须移回以填充空白。因此,如果
对象
向量很大,这将很慢。如果改用
标准::列表
(或者您已经在使用它),那么这不是问题,但列表还有其他缺点

第二种方法是用
nullptr
覆盖每个指向已删除对象的指针,然后在循环结束时使用
std::remove_,如果
,则一次删除所有指针。例如:

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;
  } 
}
这一次的假设是,由于某种原因,您将永远不会拥有
对象
nullptr
元素

由于您似乎是初学者,我应该注意:

  std::erase(std::remove_if(objects.begin(), objects.end(), 
                            [](const Object* o) { return o == nullptr; }),
             objects.end());
是。当给定函数对象被调用时,如果返回true,则从向量中删除元素。在这种情况下,函数对象为:

[](const Object* o) { return o == nullptr; }
它是一种类型,本质上是此类型对象实例的简写:

class IsNull {
 public:
   bool operator() (const Object* o) const {
     return o == nullptr;
   }
};
第二种方法的最后一个警告是,我刚刚注意到在
scrollUpdate
中的
对象上有另一个循环。如果选择第二种方法,请确保更新此循环以检查
对象中的
nullptr
并跳过它们。

这里有一个问题(格式化为可读性):

您有一个带有迭代器
p
的循环,它指向
对象
向量中的元素。当您调用
对象时,推回
,向量的迭代器可能会失效。因此循环迭代器
p
不再有效。在
for()
中增加它将导致未定义的行为

解决此问题的一种方法是创建一个临时向量来保存更新。然后在处理结束时添加更新:

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;
    }
}
void引擎::更新(字符串命令)
{
std::向量子向量;
如果(getGameOver()==false)
{
对于(p=objects.begin();p!=objects.end();p++)
{
生成更新(命令,子向量);
//...
}
}
//向向量添加元素
insert(object.end(),subVector.begin(),subVector.end());
}
无效引擎::生成更新(字符串命令,std::vector和subV)
{
if(command.compare(“shoot”)==0)
{
coutgetState());

cout您可以通过不使用原始指针来避免这些问题。很明显,您的代码使用了向量拥有指针的语义,因此您可以直接表达:

std::vector< std::unique_ptr<Object> > objects;
注意-
自动&
在这里是允许的,因为C++14;在C++11中,您必须使用
std::unique\u ptr&
。必需的包括


并且请停止使用全局迭代器,使函数保持
p
本地,并传递任何需要传递的参数。

您需要从
std::vector
中删除已删除的参数,否则下次您将测试未分配内存。否则,您可以将它们设置为
nullptr
并检查
nullptrstd::vector
中删除所有
nullptr
元素)。首先,命名变量要比
p
q
更具描述性。其次,代码的格式要稍微好一点,因为代码是h
std::vector< std::unique_ptr<Object> > objects;
objects.erase( std::remove_if( begin(objects), end(objects),
    [](auto&& o) { return o->getIsDead(); }),  end(objects) );