C++多态与动态类型转换实践困境及优化方案探讨
自研游戏引擎文件系统设计:多态与避免动态类型转换的难题
问题描述
游戏引擎中有多种对象(如Texture、Mesh等),均继承自Object类:
class Object { }; class Texture : public Object { };
我设计了用于存储对象的File类:
class File { public: Object* GetObject() const { return m_Object; } private: Object* m_Object; };
需要为存储特定类型对象的文件定义命令类FileCommand:
class FileCommand { public: virtual void Execute(File* file) = 0; };
例如OpenFileInTextureEditorCommand类需要处理存储纹理的文件,此时必须将file->GetObject()返回的Object*转换为Texture*:
class OpenFileInTextureEditorCommand : public FileCommand { public: void Execute(File* file) override { Texture* texture = std::dynamic_cast<Texture*>(file->GetObject()); if (texture) { // 在纹理编辑器中打开纹理... } } };
解决方案
我设计的方案是把动态类型转换隐藏起来:
复用Object和Texture类,将File改为模板类,同时创建非模板基类FileBase以支持存入std::vector,File类负责处理动态类型转换:
class FileBase { public: Object* GetObject() const { return m_Object; } protected: Object* m_Object = nullptr; }; template<class T> class File : protected FileBase { public: // 将类设为抽象类——无法实例化,仅负责处理动态类型转换 virtual ~File<T>() = default; T* GetObject() const { // 隐藏的动态类型转换 return dynamic_cast<T*>(m_Object); } };
同样将FileCommand改为模板类,创建非模板基类FileCommandBase以支持存入std::vector,FileCommand类负责处理动态类型转换:
class FileCommandBase { public: virtual void Execute(FileBase* file) = 0; }; template<class T> class FileCommand : protected FileCommandBase { public: // 将类设为抽象类——无法实例化,仅负责处理动态类型转换 virtual void Execute(File<T>* file) = 0; protected: void Execute(FileBase* file) override { // 隐藏的动态类型转换 Execute(dynamic_cast<File<T>*>(file)); } };
最终实现的OpenFileInTextureEditorCommand无需显式进行动态类型转换:
class OpenFileInTextureEditorCommand : public FileCommand<Texture> { public: void Execute(File<Texture>* file) override { Texture* texture = file->GetObject(); if (texture) { // 在纹理编辑器中打开纹理... } } };
总结
OpenFileInTextureEditorCommand的代码长度没变化,但逻辑更简洁。不过也发现了一些问题:
- 动态类型转换的次数反而变多了,只是从显式变成了隐式
- 新增了更多类,可能会提升维护难度
我可能过于执着于避免动态类型转换,但常听人说动态类型转换的存在是设计不佳的信号。
内容的提问来源于stack exchange,提问作者Anthony Blanchette-Potvin
相关产品推荐
相关产品推荐

