7

我想管理一堆从公共容器中的共享接口类派生的类对象。

为了说明这个问题,假设我正在构建一个包含不同演员的游戏。让我们调用接口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 Container、dynamic_cast),但我无法决定哪种方法可以去这里。

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

非常感谢帮助:)。

4

4 回答 4

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();
     }
}

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

于 2010-03-22T02:39:04.303 回答
4

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

于 2010-03-22T01:41:10.197 回答
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));
于 2010-03-22T14:27:08.423 回答
3

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

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

void*需要您进行手动内存管理,所以就不行了。当类型通过继承相关时,Boost.Any 是多余的 - 标准多态性可以完成这项工作。

您是否需要dynamic_cast是一个正交问题 - 如果容器的用户只需要 IActor 接口,并且您要么 (a) 将接口的所有功能设为虚拟,要么 (b) 使用非虚拟接口惯用语,那么你不需要dynamic_cast。如果容器的用户知道某些 IActor 对象是“真正的”平民,并且想要使用 Civilian 界面中的东西而不是 IActor,那么您将需要演员表(或重新设计)。

于 2010-03-22T11:56:28.483 回答