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

sve*_*aro 7 c++ containers interface heterogeneous

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

为了说明这个问题,我们假设我正在构建一个包含不同演员的游戏.让我们来调用接口IActor,并获得EnemyCivilian如此.

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

// somewhere during init
std::vector<IActor> ActorList;
Enemy EvilGuy; 
Civilian CoolGuy;
ActorList.push_back(EvilGuy);
ActorList.push_back(CoolGuy);
Run Code Online (Sandbox Code Playgroud)

// main loop
while(!done) {
    BOOST_FOREACH(IActor CurrentActor, ActorList) {
        CurrentActor.Update();
        CurrentActor.Draw();
    }
}
Run Code Online (Sandbox Code Playgroud)

... 或类似的规定.这个例子显然不起作用,但这就是我在这里问的原因.

我想知道:在一个通用的异构容器中管理这些对象的最佳,最安全,最高级的方法是什么?我知道各种方法(Boost :: Any,void*,带有boost :: shared_ptr的处理程序类,Boost.Pointer容器,dynamic_cast)但我无法确定哪种方法可以去这里.

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

非常感谢:).

Yog*_*ora 10

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

  • 使用虚函数定义一个基类(您已经在做),这些函数将被派生类EnemyCivilian您的情况所覆盖.
  • 您需要选择一个适当的容器来存储您的对象.你选了一个std::vector<IActor>不是一个好选择的因为
    • 首先,当您向向量添加对象时,它将导致对象切片.这意味着只存储或存储的IActor部分而不是整个对象.EnemyCivilian
    • 其次,您需要根据object(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()));
Run Code Online (Sandbox Code Playgroud)

// main loop
while(!done) 
{
    BOOST_FOREACH(some_smart_ptr<IActor> CurrentActor, ActorList) 
    {
        CurrentActor->Update();
        CurrentActor->Draw();
     }
}
Run Code Online (Sandbox Code Playgroud)

除了智能指针部分之外,它与原始代码非常相似

  • 特别是你想要一个带有复制语义的智能指针 (3认同)

Mar*_*ork 3

正如您所猜测的,您需要将对象存储为指针。
我更喜欢使用 boost 指针容器(而不是普通的智能指针容器)。

原因是 boost ptr 容器访问对象就像它们是对象(返回引用)而不是指针一样。这使得在容器上使用标准函子和算法变得更加容易。

智能指针的缺点是您共享所有权。
这不是你真正想要的。您希望所有权位于一个地方(在本例中为容器)。

boost::ptr_vector<IActor> ActorList; 
ActorList.push_back(new Enemy()); 
ActorList.push_back(new Civilian());
Run Code Online (Sandbox Code Playgroud)

std::for_each(ActorList.begin(), 
              ActorList.end(),
              std::mem_fun_ref(&IActor::updateDraw));
Run Code Online (Sandbox Code Playgroud)