我一直在努力解决一个相当令人困惑的问题:当我的实体管理器遍历更新循环中的实体映射时,偶尔会出现分段错误。奇怪的是,这不会一直发生;有时它会在加载时崩溃,有时我可以在我获得segfault之前在应用程序状态(以及多次加载和卸载实体)之间切换几次。我似乎也在调试模式下获得了更多的段错误。我的实体由指向Behavior和Drawable类的指针组成。
我在segfault之后调用堆栈:
#0 6FCB4986 libstdc++-6!_ZSt18_Rb_tree_incrementPSt18_Rb_tree_node_base() (C:\MinGW\bin\libstdc++-6.dll:??)
#1 0040A1D7 std::_Rb_tree_iterator<std::pair<unsigned int const, Entity*> >::operator++(this=0x28fe94) (c:/mingw/bin/../lib/gcc/mingw32/4.6.2/include/c++/bits/stl_tree.h:196)
#2 00401F55 EntityManager::onLoop(this=0x417238) (C:\Users\Nelarius\Documents\Kurssit\Miinaharava\src\engine\EntityManager.cpp:75)
#3 00401640 App::onLoop(this=0x417040) (C:\Users\Nelarius\Documents\Kurssit\Miinaharava\src\engine\App.cpp:38)
#4 0040160C App::execute(this=0x417040) (C:\Users\Nelarius\Documents\Kurssit\Miinaharava\src\engine\App.cpp:30)
#5 00403BD7 main(argc=1, argv=0x642908) (C:\Users\Nelarius\Documents\Kurssit\Miinaharava\src\main.cpp:15)
这是我的更新循环:
void EntityManager::onLoop()
{
std::map<const unsigned int, Entity*>::iterator it;
for (it = _gameObjects.begin(); it != _gameObjects.end(); it++)
{
Behavior* behavior = it->second->getBehavior();
if (behavior)
{
behavior->update();
}
}
}
我在
处获得了段错误for (it = _gameObjects.begin(); it != _gameObjects.end(); it++)
顺便说一下,当我没有使用任何多线程时,有两个线程是正常的吗?我正在查看Code::Blocks
调试窗口,碰巧看到线程监视窗口中有两个线程(虽然其中只有一个是活动的)。
答案 0 :(得分:2)
通常这种事情归结为behavior->update()
能够通过一系列嵌套函数调用在被修改的_gameObjects
容器中产生(例如,如果某些条件在无论出于何种原因,游戏对象都会导致游戏对象的创建或删除。
如果你从map
删除了一个元素,那么可能会使你的迭代器失效并打破你的循环,并且很难在这样的“内核”代码中找到它。
常见的解决方案是复制循环的游戏对象列表。当然,您不会复制对象本身,但是您保护它们的列表不会在更新运行过程中发生变异。
在调度方面它也是“更公平” - 你基本上避免了由不满的自我复制游戏对象发起的DDoS攻击的可能性。 :)
答案 1 :(得分:0)
您正在迭代非本地数据,然后调用非本地函数。
创建本地地图。 swap
类映射到它。迭代那个本地地图。在迭代完成后断言类映射是不变的(你还需要断言所有从类映射中删除东西的尝试都成功)。
然后将本地地图交换回班级地图。
这将告诉您是否因为糟糕的设计而导致崩溃。即使这不产生任何断言,设计仍然存在问题,因为远离上述代码的无害代码变化可能导致上述问题发生:代码正确性的非局部性导致问题。通过充分的测试,可以容忍代码正确性与断言的非本地化。
一般的回调问题要求您妥协一下注册回调意味着什么,并且需要以类似于多线程代码的方式思考问题。如果您不愿意妥协,代码复杂性可能会非常高。建议使用智能指针来简化操作。