这篇文章不重复。首先阅读我的问题。
我在这里肯定遗漏了一些东西。我希望我的Entity
课能够在健康数量为零时销毁自己的实例。
virtual int takeDamage(int damage) {
health -= damage;
if(health <= 0) delete this;
}
上述代码有什么问题?当调用上面的最后一行时,程序崩溃并出现段错误。 到目前为止,我已经尝试过:
制作析构函数:
virtual void destroy(Entity * e){ 删除e; }
销毁分配的对象
我删除了takeDamage()
的最后一行,然后从delete object
调用main
。
if(tris[i]->getHealth() <=0) delete tris[i]; //called from the main function where a temporary collision detection occurs over all the entities in the program
我找到了一些问题,它归结为以下几点。我的程序中所有Entity
(s)的实例(一个特定的专业化,即:三角形)存储在vector
中。例如,上面的代码使用此向量:vector<Triangle*> tris;
(Triangle
继承Entity
)。然后,迭代该向量并执行各种操作,例如碰撞检测,AI等。现在,当删除其中一个对象时,下次我们遍历整个向量时,我们来到具有的对象被删除了。接下来的问题是,如何缩小该向量? (现在这个是一个标记问题的好地方,可能需要一个单独的问题!)
答案 0 :(得分:1)
根据您的描述,有一种方法可以安全地使用算法函数。您可能正在寻找的算法函数是std::stable_partition
,for_each
和erase
。
假设您有“游戏循环”,并且您正在测试循环期间的碰撞:
#include <algorithm>
#include <vector>
//...
std::vector<Entity*> allEntities;
//...
while (game_is_stlll_going())
{
auto it = allEntities.begin();
while (it != allEntitities.end())
{
int damage = 0;
// ... assume that damage has a value now.
//...
// call the takeDamage() function
it->takeDamage(damage);
++it;
}
// Now partition off the items that have no health
auto damaged = std::stable_partition(allEntities.begin(),
allEntities.end(), [](Entity* e) { return e->health > 0; });
// call "delete" on each item that has no health
std::for_each(damaged, allEntities.end(), [] (Entity *e) { delete e; });
// erase the non-health items from the vector.
allEntities.erase(damaged, allEntities.end());
// continue the game...
}
基本上循环的作用是我们遍历所有实体并为每个实体调用takeDamage
。我们在此阶段不会删除任何实体。循环完成后,我们通过使用std::stable_partition
算法函数对损坏的项目进行分区来检查哪些项目没有健康状况。
为什么stable_partition
而不只是致电std::remove
或std::remove_if
并在删除项目之前,请在已移除的项目上调用delete
?原因是使用remove / remove_if
,您无法执行多步删除过程(调用delete
然后从向量中删除它)。 remove/remove_if
算法函数假定已移动到容器末尾的内容(即“已删除”项目)不再需要,因此除了最后调用vector::erase
之外,不能用于任何内容。在已删除的项目上调用delete
是未定义的行为。
因此,要解决此问题,您需要首先“分区”坏项,为每个项解除内存,然后 删除它们。我们需要使用分区算法,因此我们可以选择std::stable_partition
或std::partition
。哪一个?我们选择std::stable_partition
。我们选择std::stable_partition
超过std::partition
的原因是为了保持项目的相对顺序不变。项目的顺序对于您的游戏实施可能很重要。
现在我们调用stable_partition
将项目划分为实体向量的两侧 - 分区左侧的好项目,分区右侧的坏项目。 lambda函数用于通过测试health
值来决定哪个实体去哪里。在这种情况下,“分区”是由stable_partition
返回的迭代器。
鉴于此,我们调用for_each
,其中lambda在分区右侧的每个项目上调用delete
,然后调用vector::erase()
以删除分区右侧的所有内容。然后我们再次循环,重做整个过程直到游戏结束。
您会注意到的另一件事是上面代码的安全性。如果所有条目都具有一定的健康状况,则对stable_partition
,for_each
和erase
的调用基本上是无操作。因此,没有必要明确检查至少一个没有健康状况的项目(但如果你觉得需要这种微优化,那么没有什么能阻止你这样做。)
另外,请确保您的Entity
基类具有virtual destructor
,否则您的代码将在删除时显示未定义的行为。
修改:应将takeDamage()
函数重写为而非调用delete this
。
virtual int takeDamage(int damage) {
health -= damage;
return 0;
}