2010-03-22 33 views
7

我想管理从共同容器中的共享接口类派生的类的一堆对象。将通用接口派生的类的对象存储在公共容器中的最安全方法是什么?

为了说明问题,假设我正在构建一个包含不同演员的游戏。我们称之为接口IActor,并从中推导出EnemyCivilian。现在

,这个想法是让我的游戏主循环能够做到这一点:

// 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 Container,dynamic_cast),但我无法决定哪种方法可以到达此处。

另外我想强调的是,我想尽可能远离手动内存管理或嵌套指针。

非常感谢:)。

回答

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)); 
+0

喜欢你使用for_each的方式 – 2010-03-22 17:37:28

+0

你能解释一下你的for_each以及如何使用BOOST_FOREACH来使用它吗? – Svenstaro 2010-03-23 03:08:48

+0

std :: for_each(I1,I2,Action)。将Action应用于I1和I2(不包括I2)范围内的所有值(在本例中调用方法updateDraw)。 I1,I2是迭代器。请参阅:http://www.sgi.com/tech/stl/for_each.html – 2010-03-23 14:55:04

4

我的即时反应是你应该在容器中存储智能指针,并确保基类定义了足够的(纯粹的)虚拟方法,你永远不需要返回派生类dynamic_cast

10

为了解决你所提到的问题,尽管你正朝着正确的方向前进,但是你做错了方向。这就是你需要做的

  • 定义一个基类(你已经这样做),与将被派生类Enemy,并在你的情况Civilian覆盖虚函数的东西。
  • 你需要选择一个合适的容器来存储你的对象。你已经采取了std::vector<IActor>这不是一个好的选择,因为
    • 首先,当您向矢量添加对象时,它会导致对象切片。这意味着只有IActor部分的EnemyCivilian被存储而不是整个对象。
    • 其次,您需要根据对象的类型(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(); 
    } 
} 

这是非常相似您的原始代码除了智能指针部分

+3

具体情况,你想拷贝语义的智能指针 – 2010-03-22 13:32:32

+0

烨我与 – 2010-03-22 13:45:05

+1

同意我喜欢你的方法,因为它看起来很干净基于一个方法的指针。然而,正如其他人指出的那样,我想要做的事情可能会更好地使用Boost指针容器来完成,因为它们确实看起来正是为了我想要达到的目的而制作的。如果失败,我会尝试你的方法。 或者你看到任何理由*不*使用升压指针容器? – Svenstaro 2010-03-23 03:12:24

3

如果您希望容器独占拥有其中的元素,请使用Boost指针容器:它们是专为这项工作而设计。否则,请使用shared_ptr<IActor>的容器(并且当然要正确使用它们,这意味着需要共享所有权的每个人都使用shared_ptr)。

在这两种情况下,请确保IActor的析构函数是虚拟的。

void*要求你做手动内存管理,这样就没有了。 Boost.Any在类型通过继承关联时是过度的 - 标准多态是完成这项工作的。

无论您是否需要dynamic_cast都是一个正交问题 - 如果容器的用户只需要IActor接口,并且您(a)使接口的所有功能都为虚拟,否则(b)使用非 - 虚拟接口习惯用法,那么你不需要dynamic_cast。如果容器的用户知道一些IActor对象是“真正的”平民,并且想要利用民间界面而不是IActor的东西,那么你需要施放(或重新设计)。

相关问题