C++中的数据继承
data inheritance in C++
我有两个类,一个用于存储基本数据,另一个用于存储其他数据,如下所示:
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])
多态性在C++中的工作方式是,如果B
是基类并且D
是从B
派生的,则:
- 指向
D
的指针可用于需要指向B
的指针 - 对
D
的引用可用于预期对B
的引用
C++中不能做的实际上是在需要B
类型的值的上下文中使用类型D
的值。例如,不能将派生对象存储在基对象数组中。当您认为派生对象可能与基对象具有不同的大小时,这是有意义的。
同样,不能将派生对象存储在基对象的向量中。
您可以做的是将指向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
是一个局部变量,因此它将在其作用域结束时自动销毁。
但这可能是一种行不通的方法,因为您可能希望向量元素引用的对象持续到当前范围之外。我们可以为此目的使用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());
(但请注意,您无法复制此向量;只能移动它。
基本上,您需要对指向的对象使用某种引用,因为您需要动态多态性。
最简单但容易出错的是使用"裸"指针。首先有问题的是你必须手动进行销毁:容器将销毁指针,而不是指向的内容。
执行此操作更安全的方法是使用智能指针,这些指针旨在根据智能指针嵌入其类型的预先固定的规则进行销毁。如果你怀疑的话,最简单的一个,当然也是最好的选择是std::unique_ptr,它不能复制,但可以移动。另一个选择,在使用之前应该仔细考虑,是std::shared_ptr这是有用的IFF,你不知道什么时候应该销毁这些对象,但你知道这是一些系统不再引用它的时候。其他一些系统可能只是在观察该对象,在这种情况下 std::weak_ptr。
现在,通过阅读您的问题,我认为您肯定正在处理很多这些动画数据。那里有一个明显的设计问题,我想,我可能是错的。 但是,看起来,如果你有很多这样的AnimationState要管理,在一个循环中,你会遇到性能问题。这是游戏中的常见问题,主要是由"缓存一致性"引起的。
在这种情况下,我会建议不要使用
- 继承:它邀请CPU到处跳转并触发缓存未命中;
- dynamic_cast:这是少数几个不能保证在可预测的时间内结束的操作之一(例如,使用 new 和 delete),这基本上意味着如果您处于关键循环中,您可能会为此浪费大量时间。在某些情况下,您无法避免使用动态强制转换(例如在执行动态插件时),但在大多数情况下,仅仅因为您选择使用继承而使用它是错误的。如果使用继承,则应使用虚拟调用。
然而,我的建议更加激烈:根本不使用继承。
显然,这只是一个建议。如果你没有做一些关键循环的事情,那也没关系。我只是担心,因为看起来您正在为组合做一些继承,这总是对代码的可读性和性能产生不良后果。
- 继承的数据如何在派生类中放置(排列)?
- 无法将数据从父类继承到C++中的子类
- 重载<<无法访问继承的私有数据
- 将数据添加到现有类,而无需继承、映射或修改类
- 派生类不从基类继承数据成员
- 虚拟继承的性能开销(如果只有一个基具有数据成员)
- C 继承访问受保护的数据成员
- 继承的类对象如何使用私有数据成员
- 子类是否也在 c++ 中继承私有数据成员?但通过超类的公共方法访问
- 如何在继承级别的多个实例之间共享相同的数据
- 子进程如何使用 fork() 从父进程继承数据
- 结构绑定:与公共数据成员(继承的基类)结合
- C++中的数据继承
- C 数据结构代表单个继承
- 基础和派生类C 之间的继承数据成员
- 初始化继承的成员数据
- 指向具有继承类型的类数据成员的指针
- 继承的类数据成员
- 在C++继承中,派生类如何禁用基类的某些元素(数据或函数)
- 子级从父级继承数据的嵌套数据结构