什么会是最安全的方式存储对象的类别来自一个共同的接口在一个共同的容器?
-
21-09-2019 - |
题
我想要管理一堆的对象的类别来自一个共用的界面类在一个共同的容器。
为了说明问题,让我们说我是建设一个游戏其中将包含不同行为者。让我们打电话的接口 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();
}
}
...或者沿着这些线路的东西。这个例子显然不会工作,但是很多的原因,我要在这里。
我想知道:什么是最好、最安全、最高级别的方式来管理这些对象在一个共同的异构的容器?我知道关于各种各样的方法(提升::任何,void*,处理程序类与提升::情况,提高.指针的容器,dynamic_cast)但是我不能决定它将以何种方式去这里。
此外,我想强调,我想要停留,尽可能从手册管理存储器或嵌套指针。
有太大的帮助认识到:).
解决方案
正如你已经猜到了,你需要存储的对象的指针。结果 我更喜欢使用升压指针容器(而不是智能指针的正常容器)。
这样做的原因是升压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));
其他提示
要解决的问题,你必须提及的,虽然你是在正确的方向,但你这样做是错误的方式。这就是你所需要做的
- 定义一个基类(其中你已经做)与的虚拟的功能,将能复盖的源类
Enemy
和Civilian
在你的情况。 - 你需要选择一个适当的容器将保存你的对象。你已经采取了一个
std::vector<IActor>
这不是一个好的选择,因为- 首先当你加入对象的矢量,它是导致对象的切片。这意味着只有
IActor
部分的Enemy
或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()));
和
// main loop
while(!done)
{
BOOST_FOREACH(some_smart_ptr<IActor> CurrentActor, ActorList)
{
CurrentActor->Update();
CurrentActor->Draw();
}
}
这是非常相似的你的原始代码,除了明智的指针的部分
我的即时反应是,你应该存储在容器智能指针,并确保基类定义了足够的(纯)虚方法,你永远需要dynamic_cast
回派生类。
如果您希望容器完全拥有它的元素,使用升压指针容器:他们正在设计用于工作。否则,使用shared_ptr<IActor>
的容器(当然,正确地使用它们,这意味着大家谁需要股权用途shared_ptr
)。
在这两种情况下,确保IActor
的析构函数是虚拟的。
void*
需要你做手工的内存管理,让出去了。 Boost.Any是矫枉过正当类型继承关系 - 标准多态性做这项工作
无论您需要dynamic_cast
与否是正交的问题 - 如果容器的用户只需要IActor界面,你是(a)使接口的所有功能的虚拟,或者(b)利用非虚拟接口成语,那么你并不需要的dynamic_cast。如果容器知道的用户,一些IActor对象是“真正的”平民,并想利用的东西是在民用接口,但不IActor,那么你就需要强制类型转换(或重新设计)。