如何在C++中以类类型作为map的值传入并实例化对象?
问题描述
现有一段意图实现动物园动物实例化的C++代码,但其中ClassType无合法定义,核心需求是:调用Zoo::Build方法时,将Animal子类的类类型作为std::map的值传入,在Build内部通过new Class()完成对象实例化:
class Animal { public: virtual std::string Say() = 0; }; class Mouse : public Animal { public: std::string Say() { return "I am a mouse."; } }; class Duck : public Animal { public: std::string Say() { return "I am a duck."; } }; class Dog : public Animal { public: std::string Say() { return "I am a dog."; } }; class Zoo { public: void Build(std::map<std::string, ClassType> animals) { for (std::map<std::string, ClassType>::iterator it = animals.begin(); it != animals.end(); it++) { this->animals[it->first] = new it->second(); } } private: std::map<std::string, Animal*> animals; }; int main() { Zoo zoo; zoo.Build({{"Mickey", Mouse}, {"Daisy", Duck}, {"Goofy", Dog}}); return 0; }
可行实现方案
方案一:函数指针(C++03兼容,最简写法)
定义返回Animal*的函数指针类型,为每个子类编写对应的工厂函数,将函数指针存入map作为值传递:
#include <map> #include <string> #include <cstdio> class Animal { public: virtual std::string Say() = 0; virtual ~Animal() = default; // 基类必须加虚析构,否则子类对象无法正确销毁,会漏内存 }; class Mouse : public Animal { public: std::string Say() override { return "I am a mouse."; } }; class Duck : public Animal { public: std::string Say() override { return "I am a duck."; } }; class Dog : public Animal { public: std::string Say() override { return "I am a dog."; } }; // 定义工厂函数指针类型 using AnimalFactory = Animal* (*)(); // 为每个子类写工厂函数 Animal* CreateMouse() { return new Mouse(); } Animal* CreateDuck() { return new Duck(); } Animal* CreateDog() { return new Dog(); } class Zoo { public: void Build(const std::map<std::string, AnimalFactory>& factories) { // 先清掉原有对象,避免重复实例化导致内存泄漏 for (auto& pair : animals) { delete pair.second; } animals.clear(); for (const auto& pair : factories) { animals[pair.first] = pair.second(); } } // 测试方法:让所有动物说话 void SpeakAll() { for (const auto& pair : animals) { printf("%s: %s\n", pair.first.c_str(), pair.second->Say().c_str()); } } ~Zoo() { // 析构时清理所有动物对象 for (auto& pair : animals) { delete pair.second; } } private: std::map<std::string, Animal*> animals; }; int main() { Zoo zoo; zoo.Build({ {"Mickey", CreateMouse}, {"Daisy", CreateDuck}, {"Goofy", CreateDog} }); zoo.SpeakAll(); return 0; }
- 必须给基类
Animal加虚析构函数,否则子类对象销毁时只会调用基类析构,导致内存泄漏 - 工厂函数统一返回基类指针,符合C++多态规则
- Build方法里先清理旧对象,避免内存堆积
方案二:std::function(C++11+,更灵活)
用std::function替代原始函数指针,支持lambda、绑定表达式等多种可调用对象,不需要单独写工厂函数:
#include <map> #include <string> #include <functional> #include <cstdio> class Animal { public: virtual std::string Say() = 0; virtual ~Animal() = default; }; class Mouse : public Animal { public: std::string Say() override { return "I am a mouse."; } }; class Duck : public Animal { public: std::string Say() override { return "I am a duck."; } }; class Dog : public Animal { public: std::string Say() override { return "I am a dog."; } }; class Zoo { public: // 定义工厂类型为返回Animal*的可调用对象 using AnimalFactory = std::function<Animal*()>; void Build(const std::map<std::string, AnimalFactory>& factories) { // 清理旧对象 for (auto& pair : animals) { delete pair.second; } animals.clear(); for (const auto& pair : factories) { animals[pair.first] = pair.second(); } } void SpeakAll() { for (const auto& pair : animals) { printf("%s: %s\n", pair.first.c_str(), pair.second->Say().c_str()); } } ~Zoo() { for (auto& pair : animals) { delete pair.second; } } private: std::map<std::string, Animal*> animals; }; int main() { Zoo zoo; zoo.Build({ {"Mickey", []() { return new Mouse(); }}, {"Daisy", []() { return new Duck(); }}, {"Goofy", []() { return new Dog(); }} }); zoo.SpeakAll(); return 0; }
std::function比原始函数指针更灵活,支持直接用lambda表达式生成工厂,不需要额外写函数- 同样要注意虚析构和内存清理的问题
方案三:模板+std::type_index(贴近"传入类类型"的需求)
如果想直接传递类类型而非工厂函数,可以结合模板和std::type_index实现类型到工厂的映射:
#include <map> #include <string> #include <typeindex> #include <functional> #include <cstdio> #include <type_traits> class Animal { public: virtual std::string Say() = 0; virtual ~Animal() = default; }; class Mouse : public Animal { public: std::string Say() override { return "I am a mouse."; } }; class Duck : public Animal { public: std::string Say() override { return "I am a duck."; } }; class Dog : public Animal { public: std::string Say() override { return "I am a dog."; } }; // 全局注册所有Animal子类对应的工厂函数 std::map<std::type_index, std::function<Animal*()>> type_factory_map = { {typeid(Mouse), []() { return new Mouse(); }}, {typeid(Duck), []() { return new Duck(); }}, {typeid(Dog), []() { return new Dog(); }} }; class Zoo { public: // 模板方法:直接传入类类型添加动物 template<typename T> void AddAnimal(const std::string& name) { // 静态断言:确保T是Animal的子类 static_assert(std::is_base_of<Animal, T>::value, "T must inherit from Animal"); animals[name] = type_factory_map[typeid(T)](); } // 批量Build方法:接收名称与类类型的映射 void Build(const std::initializer_list<std::pair<std::string, std::type_index>>& animal_types) { // 清理旧对象 for (auto& pair : animals) { delete pair.second; } animals.clear(); for (const auto& pair : animal_types) { animals[pair.first] = type_factory_map[pair.second](); } } void SpeakAll() { for (const auto& pair : animals) { printf("%s: %s\n", pair.first.c_str(), pair.second->Say().c_str()); } } ~Zoo() { for (auto& pair : animals) { delete pair.second; } } private: std::map<std::string, Animal*> animals; }; int main() { Zoo zoo; // 方式1:单个添加(直接传类类型) zoo.AddAnimal<Mouse>("Mickey"); zoo.AddAnimal<Duck>("Daisy"); zoo.AddAnimal<Dog>("Goofy"); // 方式2:批量Build // zoo.Build({ // {"Mickey", typeid(Mouse)}, // {"Daisy", typeid(Duck)}, // {"Goofy", typeid(Dog)} // }); zoo.SpeakAll(); return 0; }
- 通过
std::type_index将类类型转换为可作为map键的类型,绑定对应的工厂函数 - 模板方法
AddAnimal提供类型安全的添加方式,静态断言防止传入非Animal子类 - 需要预先注册所有可能用到的Animal子类
内容的提问来源于stack exchange,提问作者wilson.liu
相关产品推荐
相关产品推荐

