在公共容器中存储从公共接口派生的类的对象最安全的方法是什么?

时间:2010-03-22 01:24:25

标签: c++ interface containers heterogeneous

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

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

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

非常感谢:)。

4 个答案:

答案 0 :(得分:10)

要解决你提到的问题,虽然你正朝着正确的方向前进,但你做错了。这就是你需要做的事情

  • 使用虚拟函数定义一个基类(您已经在做),在您的情况下,这些函数将被派生类EnemyCivilian覆盖。
  • 您需要选择一个适当的容器来存储您的对象。你选了std::vector<IActor>这不是一个好选择,因为
    • 首先,当您向对象添加对象时,它会导致对象切片。这意味着只存储IActorEnemy的{​​{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));