【问题标题】:removing Object from a vector of pointer, 2D array of pointers and freeing its memory in one loop从指针向量、二维指针数组中删除对象并在一个循环中释放其内存
【发布时间】:2021-01-20 15:36:21
【问题描述】:

在我的应用程序中,我有一个可以包含 Agent 对象的 2D Environment。为了简化计算,我的环境由两种数据结构组成:

  1. Agent*** grid 表示 2D 指针网格(如果代理位于某个位置,则有指向它的指针,否则为 nullptr)。
  2. std::vector<Agent*> agents 表示环境中的代理列表。当我需要更新代理时,我使用此数据结构来防止遍历整个网格。

每次我更新整个环境时,我都想根据一个标准从环境中删除我的一部分代理。因此,我想从上面描述的两个数据结构中删除代理,然后释放代理内存。另外,为了最大限度地提高效率,由于agents 可能是一个非常大的向量,我想在一个循环中执行删除和delete

我的以下(和不正确的)方法如下:

void Environment::removeDeadAgents(){
std::vector<Agent*>::iterator it = agents.begin();

while(it != agents.end()){
    Agent* a = (*it);
    if(!a->isAlive()){
       grid[a->getY()][a->getX()] = nullptr;
       delete *it;
       it = agents.erase(it);
    }else{
        it++;
    }
}

我错过了什么?

【问题讨论】:

  • 你说你的方法不正确,具体是什么症状?您可以在这里按要求提供minimal reproducible example 吗?
  • 无关:随便看看,你会发现整个 Internet 站点都专门用来嘲笑三星级程序员。
  • 我凭经验发现每次使用三指针时,几乎都是因为我的设计不好。
  • 注意:这里有什么东西很容易出错。您是否考虑过使用智能指针? shared_ptrweak_ptr 会更合适,但我认为你可以使用 vector 拥有的 unique_ptrs 来做到这一点。
  • 另外,为了最大限度地提高效率,由于代理可能是一个非常大的向量,我想在一个循环中执行删除和删除。 -- 你的方法不是完全最优。每次调用erase,都必须缩小大向量。有很多更好的方法可以做到这一点而无需花费(直到最后)。

标签: c++ pointers iterator stdvector


【解决方案1】:

您的方法可以重写,但使用std::stable_partition 会更安全

#include <algorithm>

void Environment::removeDeadAgents()
{
   // partition the vector, alive to the left of the partition, 
   // dead to the right of the partition.  An iterator to the partitioning
   // point is returned.
   auto iter = std::stable_partition(agents.begin(), agents.end(), 
                                     [](Agent *a) { return a->isAlive();});

   // null out all the dead ones in the grid and call delete.
   std::for_each(iter, agents.end(), 
                      [&](Agent* a) {grid[a->getY()][a->getX()] = nullptr; delete a;});

  // erase dead ones from vector.
  agents.erase(iter, agents.end());
}

正如评论所说,分区只是移动指针,将活动代理放在分区的左侧,将死代理放在分区的右侧。

然后在一个循环中,重置网格并在每个死网格上调用delete。 然后在此结束时,完成对 erase 的一次调用,而不是像您的原始代码那样多次调用 erase

另外,请注意擦除是一个单独的调用,唯一被擦除的项目是vector 末尾的那些(这就是我将死代理分区到分区右侧的原因)。从向量后面擦除项目比在向量中间或前面擦除项目效率更高。

如果agent向量中项目的顺序不重要,那么可以使用std::partition,这将比std::stable_partition效率更高。 std::partition 将简单地通过向量,并且不保持原始顺序,将活动代理和死亡代理分别放置在分区点的左侧和右侧。

如果您不知道std::partition,则原始代码的更好实现是通过遍历向量一次并将活动代理带到向量的前面并以战略方式进行交换,然后记录最后一个活着的代理被交换到的位置,然后从那里开始擦除(是的,它需要一些代码来做到这一点,但你明白了)。这实质上就是这个解决方案(使用std::partitionstd::stable_partition)正在做的事情。

【讨论】:

  • 我怀疑你是 C++ 之神,然后我读了你的简历,我的怀疑变成了确认! :P
猜你喜欢
  • 2016-07-08
  • 2015-11-14
  • 2023-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-18
相关资源
最近更新 更多