我想管理从公共容器中的共享接口类派生的一堆类对象。
为了说明问题,假设我正在构建一个包含不同演员的游戏。让我们调用接口IActor
并从中导出Enemy
和Civilian
。
现在,我的想法是让我的游戏主循环能够做到这一点:
// somewhere during init
std::vector<IActor> ActorList;
Enemy EvilGuy;
Civilian CoolGuy;
ActorList.push_back(EvilGuy);
ActorList.push_back(CoolGuy);
和
// main loop
while(!done) {
BOOST_FOREACH(IActor CurrentActor, ActorList) {
CurrentActor.Update();
CurrentActor.Draw();
}
}
......或者其他类似的东西。这个例子显然不起作用,但这就是我在这里问的原因。
我想知道:在一个通用的异构容器中管理这些对象的最佳,最安全,最高级的方法是什么?我知道各种方法(Boost :: Any,void *,带有boost :: shared_ptr的处理程序类,Boost.Pointer容器,dynamic_cast)但我无法确定哪种方法可以去这里。
另外我想强调一点,我希望尽可能远离手动内存管理或嵌套指针。
非常感谢:)。
答案 0 :(得分:10)
要解决你提到的问题,虽然你正朝着正确的方向前进,但你做错了。这就是你需要做的事情
Enemy
和Civilian
覆盖。std::vector<IActor>
这不是一个好选择,因为
IActor
或Enemy
的{{1}}部分,而不是整个对象。Civilian
)调用函数,这只有在使用指针时才会发生。 上述两个原因都表明你需要使用一个可以包含指针的容器,比如virtual functions
。但更好的选择是使用std::vector<IActor*>
,这将使您免于内存管理的麻烦。您可以根据需要使用任何智能指针(但不能使用container of smart pointers
)
这就是您的代码的样子
auto_ptr
和
// somewhere during init
std::vector<some_smart_ptr<IActor> > ActorList;
ActorList.push_back(some_smart_ptr(new Enemy()));
ActorList.push_back(some_smart_ptr(new Civilian()));
除了智能指针部分
之外,它与原始代码非常相似答案 1 :(得分:4)
我的即时反应是你应该在容器中存储智能指针,并确保基类定义了足够的(纯)虚拟方法,你永远不需要dynamic_cast
回到派生类。
答案 2 :(得分:3)
如果您希望容器专门拥有其中的元素,请使用Boost指针容器:它们是为该作业设计的。否则,请使用shared_ptr<IActor>
的容器(当然正确使用它们,这意味着每个需要共享所有权的人都使用shared_ptr
)。
在这两种情况下,请确保IActor
的析构函数是虚拟的。
void*
要求您进行手动内存管理,以便完成。当类型通过继承相关时,Boost.Any是过度的 - 标准多态性可以完成工作。
是否需要dynamic_cast
是一个正交问题 - 如果容器的用户只需要IActor接口,并且(a)使接口的所有功能都成为虚拟,或者(b)使用非虚拟接口惯用法,那么你不需要dynamic_cast。如果容器的用户知道某些IActor对象是“真正的”平民,并且想要使用Civilian界面而不是IActor的东西,那么你将需要强制转换(或重新设计)。
答案 3 :(得分:3)
正如您猜测的那样,您需要将对象存储为指针 我更喜欢使用boost指针容器(而不是智能指针的普通容器)。
原因是boost ptr容器访问对象就像它们是对象(返回引用)而不是指针一样。这使得在容器上使用标准仿函数和算法变得更加容易。
智能指针的缺点是你正在分享所有权 这不是你真正想要的。您希望所有权位于一个地方(在这种情况下是容器)。
boost::ptr_vector<IActor> ActorList;
ActorList.push_back(new Enemy());
ActorList.push_back(new Civilian());
和
std::for_each(ActorList.begin(),
ActorList.end(),
std::mem_fun_ref(&IActor::updateDraw));