C ++中的数据继承

时间:2014-02-05 03:45:31

标签: c++ inheritance polymorphism

我有两个类,一个用于存储基础数据,另一个用于存储其他数据,如下所示:

struct AnimationState(){
    virtual ~ AnimationState(){};
    Vector3f m_spacialData;
    float m_fTimeStamp;
}

派生类:

struct HermiteAnimationState() : public AnimationState{
     virtual ~HermiteAnimationState(){};
     Vector3f m_tangentIn;
     Vector3f m_tangentOut;
}

我的问题:我是如何创建 HermiteAnimationState 的实例,然后将其向上转换为 AnimationState 进行存储的像这样的矢量:

std::vector<AnimationState>     m_vStates;
...

最近,我可以获取对象 AnimationState 并将其向下转换为 HermiteAnimationState 以访问其他数据(成员m_tangentIn和m_tangentOut)。

HermiteAnimationState* p = dynamic_cast<HermiteAnimationState*>(&m_vStates[i])

2 个答案:

答案 0 :(得分:1)

多态在C ++中的工作方式是,如果B是基类而D是从B派生的,那么:

  • 指向D的指针可用于指向B的指针
  • D的引用可用于预计会引用B
  • 的情况

在C ++中无法做的事情实际上是在的上下文中使用类型为D类型B是预期的。例如,您不能将派生对象存储在基础对象的数组中。当您考虑派生对象可能与基础对象具有不同的大小时,这是有意义的。

同样,您不能将派生对象存储在基础对象的矢量中。

可以做的是将指针存储在指针HermiteAnimationState的向量中的AnimationState。这取决于你如何管理内存。例如,以下内容有效:

std::vector<AnimationState*> m_vStates;
HermiteAnimationState h_a_s;
m_vStates.push_back(&h_a_s);

...

HermiteAnimationState* p = dynamic_cast<HermiteAnimationState*>(m_vStates[i])

由于h_a_s是一个局部变量,它将在其范围的末尾自动销毁。

但这可能是 一种不可行的方法,因为你可能希望vector元素引用的对象持久存在于当前范围之外。我们可以将std::unique_ptr用于此目的。 std::unique_ptr 拥有它指向的对象,只要它保持活着,该对象也是如此;它会在它自身被销毁时删除它。因此,std::unique_ptr对象的向量在内存管理方面就像对象本身的向量。现在你可以做到

std::vector<std::unique_ptr<AnimationState*>> m_vStates;
m_vStates.emplace_back(new HermiteAnimationState);

...

HermiteAnimationState* p =
  dynamic_cast<HermiteAnimationState*>(m_vStates[i].get());

(但请注意,您无法复制此向量;您只能移动它。)

答案 1 :(得分:1)

基本上,您需要对指向对象使用某种引用,因为您需要动态多态。

最简单但容易出错的是使用“裸”指针。对此有问题的第一件事是你必须手动破坏:容器会破坏指针,而不是指向的东西。

更安全的方法是使用智能指针,这些指针旨在根据智能指针嵌入其类型的预先固定的规则进行销毁。最简单的一个,当然是最好的选择,如果你怀疑是std :: unique_ptr,它不能复制但可以移动。另一个选择,在使用之前应该仔细考虑,是std :: shared_ptr,它是有用的IFF,你不知道什么时候你应该销毁这些对象,但是你知道当某些系统不再引用它时。其他一些系统可能只是在观察那个对象,在这种情况下是std :: weak_ptr。


现在,通过阅读您的问题,我认为您肯定正在处理大量这些动画数据。那里有一个明显的设计问题,我想,我可能错了。 但是,看起来,如果要管理很多这些AnimationState,在循环中,您将遇到性能问题。这是游戏中的常见问题,主要是由“缓存一致性”引起的。

在这种情况下,我建议不要使用

  • 继承:这是一个邀请cpu跳过遍布的地方并触发缓存未命中;
  • dynamic_cast:它是少数几个无法保证在可预测的时间内结束的操作之一(例如new和delete),这基本上意味着如果你处于关键循环中,你可能会失去很多时间它。在某些情况下,您无法避免使用动态强制转换(例如在执行动态插件时),但在大多数情况下,仅仅因为您选择使用继承而使用它是错误的。如果使用继承,则应使用虚拟调用。

然而,我的建议更为激烈:根本不使用继承。

显然,这只是一个建议。如果你没有做一个关键循环的事情,那没关系。我只是担心,因为看起来你正在为组合做一些继承,这对代码的可读性和性能都会产生不良影响。