如何在C++中模拟元类?附Object Pascal示例对比
在C++中模拟Object Pascal的元类工厂机制
问题场景回顾
Object Pascal中可通过元类(class of Animal)实现灵活的工厂函数,直接传递类类型即可创建实例:
type Animal = class end; Cat = class(Animal) end; Dog = class(Animal) end; MetaAnimal = class of Animal; function factory(m: MetaAnimal): Animal; begin Result := m.create(); end; // 使用示例 var pAnimal: Animal; pAnimal := factory(Cat);
而常规C++的switch工厂方案存在扩展性缺陷:新增子类时必须修改工厂函数代码,无法自动识别外部文件中定义的子类。
方案一:标准C++注册式工厂(无ABI依赖)
这种方式无需编译器hack,通过静态注册机制让子类自动向工厂注册自身的创建逻辑,实现类似元类的灵活创建能力:
#include <unordered_map> #include <typeinfo> #include <memory> class Animal { public: virtual ~Animal() = default; }; // 工厂类,维护类型到创建函数的映射 class AnimalFactory { public: using CreateFunc = Animal*(*)(); static AnimalFactory& Instance() { static AnimalFactory factory; return factory; } void Register(const char* typeName, CreateFunc func) { creators_[typeName] = func; } Animal* Create(const char* typeName) { auto it = creators_.find(typeName); if (it != creators_.end()) { return it->second(); } return nullptr; } // 模板版本,直接传递类型创建实例 template<typename T> static Animal* Create() { return T::CreateInstance(); } private: std::unordered_map<const char*, CreateFunc> creators_; }; // 用于子类自动注册的模板基类 template<typename Derived> class AnimalRegister : public Animal { public: static Animal* CreateInstance() { return new Derived(); } protected: AnimalRegister() { AnimalFactory::Instance().Register(typeid(Derived).name(), CreateInstance); } }; // 子类定义 class Cat : public AnimalRegister<Cat> {}; class Dog : public AnimalRegister<Dog> {}; // 外部文件新增子类无需修改工厂,只需继承AnimalRegister即可 // class Cow : public AnimalRegister<Cow> {}; // 使用示例 int main() { // 方式1:通过类型名创建 Animal* cat = AnimalFactory::Instance().Create(typeid(Cat).name()); // 方式2:直接传递类型(类似Pascal的元类传递) Animal* dog = AnimalFactory::Create<Dog>(); delete cat; delete dog; return 0; }
该方案跨编译器兼容,新增子类仅需继承AnimalRegister<Derived>即可自动完成注册,无需修改工厂函数代码。
方案二:基于VMT表的ABI Hack(依赖编译器实现)
若要严格模拟Pascal中直接传递类类型的元类机制,可通过hack虚表(VMT)实现,但该方案完全依赖编译器ABI,需针对不同编译器编写适配代码。
针对GCC/Clang的实现
GCC中类的虚表数组首元素为type_info指针,后续为虚函数指针,我们可通过关联虚表地址与创建函数实现需求:
#include <iostream> #include <unordered_map> class Animal { public: virtual ~Animal() = default; // 虚函数占位,确保类生成虚表 virtual void dummy() {} }; // 元类类型定义,本质为指向虚表的指针 using MetaAnimal = void(*)(); // 维护虚表到创建函数的映射 static std::unordered_map<void*, Animal*(*)()> vmtToCreator; // 注册子类的虚表与创建函数 template<typename Derived> void RegisterMetaClass() { Derived tmp; void** vmt = *reinterpret_cast<void***>(&tmp); vmtToCreator[vmt] = []() -> Animal* { return new Derived(); }; } // 工厂函数,接收元类(虚表指针)创建实例 Animal* factory(MetaAnimal meta) { void** vmt = reinterpret_cast<void**>(meta); auto it = vmtToCreator.find(vmt); if (it != vmtToCreator.end()) { return it->second(); } return nullptr; } // 子类定义 class Cat : public Animal {}; class Dog : public Animal {}; // 静态注册子类元类 static bool catRegistered = [](){ RegisterMetaClass<Cat>(); return true; }(); static bool dogRegistered = [](){ RegisterMetaClass<Dog>(); return true; }(); // 使用示例 int main() { Cat tmpCat; MetaAnimal catMeta = reinterpret_cast<MetaAnimal>(*reinterpret_cast<void***>(&tmpCat)); Animal* cat = factory(catMeta); Dog tmpDog; MetaAnimal dogMeta = reinterpret_cast<MetaAnimal>(*reinterpret_cast<void***>(&tmpDog)); Animal* dog = factory(dogMeta); delete cat; delete dog; return 0; }
针对MSVC的实现
MSVC的虚表首元素为RTTI Complete Object Locator指针,需调整虚表获取逻辑:
#ifdef _MSC_VER #include <iostream> #include <unordered_map> class Animal { public: virtual ~Animal() = default; virtual void dummy() {} }; using MetaAnimal = void(*)(); static std::unordered_map<void*, Animal*(*)()> vmtToCreator; template<typename Derived> void RegisterMetaClass() { Derived tmp; // MSVC中对象首成员为虚表指针 void** vmt = *reinterpret_cast<void***>(&tmp); vmtToCreator[vmt] = []() -> Animal* { return new Derived(); }; } Animal* factory(MetaAnimal meta) { void** vmt = reinterpret_cast<void**>(meta); auto it = vmtToCreator.find(vmt); if (it != vmtToCreator.end()) { return it->second(); } return nullptr; } class Cat : public Animal {}; class Dog : public Animal {}; static bool catRegistered = [](){ RegisterMetaClass<Cat>(); return true; }(); static bool dogRegistered = [](){ RegisterMetaClass<Dog>(); return true; }(); int main() { Cat tmpCat; MetaAnimal catMeta = reinterpret_cast<MetaAnimal>(*reinterpret_cast<void***>(&tmpCat)); Animal* cat = factory(catMeta); Dog tmpDog; MetaAnimal dogMeta = reinterpret_cast<MetaAnimal>(*reinterpret_cast<void***>(&tmpDog)); Animal* dog = factory(dogMeta); delete cat; delete dog; return 0; } #endif
该方案兼容性差,编译器版本更新可能导致虚表结构变化,但确实能模拟Pascal中直接传递类类型的元类机制。
内容的提问来源于stack exchange,提问作者Saku
相关产品推荐
相关产品推荐

