C++能否通过函数参数指定类类型完成初始化并存储派生类实例?
实现方案说明
原有代码核心问题
- 对象切片问题:
vector<Child>存储的是基类值对象,插入子类实例时会切割掉子类独有的成员,仅保留基类部分,完全丢失子类特性。 - 无多态支持:基类
Child没有定义虚函数,无法通过基类指针调用子类的独有方法,也无法正确触发子类的析构等操作。 - 运行时类名映射缺失:C++没有内置的通过字符串类名直接创建实例的反射机制,需要手动实现工厂逻辑完成字符串到构造函数的映射。
方案1:多态+工厂模式(最通用的实现)
适合子类可扩展、接受使用时做类型判断的场景,兼容性最好:
#include <iostream> #include <vector> #include <string> #include <unordered_map> #include <functional> #include <memory> using namespace std; // 基类必须定义虚函数表,至少要有虚析构 class Child { protected: string child_name; public: virtual ~Child() = default; // 避免子类释放时内存泄漏 string getName() const { return child_name; } }; class Jame : public Child { public: Jame() { child_name = "Jame"; } void sit() { cout << "Jame can sit" << endl; } }; class Harry : public Child { public: Harry() { child_name = "Harry"; } void sleep() { cout << "Harry can stand" << endl; } }; class Parent { private: // 用智能指针存储实例,彻底避免对象切片 vector<unique_ptr<Child>> children; // 工厂映射表:类名 -> 对应实例的创建函数 static inline unordered_map<string, function<unique_ptr<Child>()>> factory_map = { {"Jame", []() { return make_unique<Jame>(); }}, {"Harry", []() { return make_unique<Harry>(); }} }; public: void addChild(const string& class_name) { auto it = factory_map.find(class_name); if (it != factory_map.end()) { children.push_back(it->second()); } } Child* getChild(const string& child_name) { for (auto& child : children) { if (child->getName() == child_name) { return child.get(); } } return nullptr; } }; int main() { Parent parent; parent.addChild("Harry"); parent.addChild("Jame"); Child* harry = parent.getChild("Harry"); // 类型安全的向下转型,判断成功再调用独有方法 if (auto* harry_ptr = dynamic_cast<Harry*>(harry)) { harry_ptr->sleep(); // 正常输出:Harry can stand } Child* jame = parent.getChild("Jame"); if (auto* jame_ptr = dynamic_cast<Jame*>(jame)) { jame_ptr->sit(); // 正常输出:Jame can sit } // 非法调用会被dynamic_cast拦截,不会执行 if (auto* wrong_ptr = dynamic_cast<Jame*>(harry)) { wrong_ptr->sit(); } return 0; }
方案要点
- 新增子类时仅需要在工厂映射表中新增一条对应记录即可,扩展性强。
dynamic_cast会做运行时类型检查,转型失败返回空指针,避免非法调用崩溃。- 智能指针自动管理实例生命周期,不会出现内存泄漏。
方案2:std::variant实现(C++17及以上适用)
如果所有子类类型编译期可确定、不需要后续扩展未知子类,可使用该方案,不需要定义虚函数也不需要手动转型:
// 前面Child、Jame、Harry类定义和上面一致,不需要虚析构也可以 class Parent { private: // 提前列出所有可能的子类类型 variant<Jame, Harry> child; vector<variant<Jame, Harry>> children; public: void addChild(const string& class_name) { if (class_name == "Jame") children.emplace_back(Jame()); else if (class_name == "Harry") children.emplace_back(Harry()); } // 直接通过visit访问对应类型的方法 template<typename Func> void visitChild(const string& child_name, Func&& func) { for (auto& c : children) { std::visit([&](auto&& arg) { if (arg.getName() == child_name) func(arg); }, c); } } }; int main() { Parent parent; parent.addChild("Harry"); parent.visitChild("Harry", [](Harry& h) { h.sleep(); // 直接调用方法不需要转型 }); return 0; }
内容的提问来源于stack exchange,提问作者KornYellow
相关产品推荐
相关产品推荐

