同一类多构造函数的工厂模式注册重定义问题求解
支持多构造函数的智能指针工厂模式改造
问题分析
当前代码存在两个核心问题:
- 静态变量重复定义:
DerivedExample中仅声明了一个static bool sign,却尝试两次定义它,触发编译错误。 - 注册表键冲突:工厂仅用类名字符串作为唯一键,两个
create函数注册时会互相覆盖,导致调用带参数的instantiate时无法找到正确的函数指针。
修改后的完整代码
#include <unordered_map> #include <string> #include <iostream> #include <memory> #include <typeindex> #include <tuple> class Base; class myFactory { public: // 用类名+参数类型组合作为注册表的键,避免不同参数构造函数的键冲突 typedef std::pair<std::string, std::type_index> KeyType; typedef std::unordered_map<KeyType, void*, std::hash<KeyType>> registry_map; virtual ~myFactory() = default; static registry_map& registry() { static registry_map impl; return impl; } template<typename... T> static std::shared_ptr<Base> instantiate(const std::string& name, T&&... args) { // 生成包含类名和参数类型元组的键 KeyType key{name, std::type_index(typeid(std::tuple<T...>))}; auto it = registry().find(key); if (it == registry().end()) return nullptr; typedef std::shared_ptr<Base> (*create_type)(T...); auto create_fun = reinterpret_cast<create_type>(it->second); return create_fun(std::forward<T>(args)...); } template<typename F, typename... T> static bool sign(const std::string& name, F func) { // 根据注册函数的参数类型生成对应键 KeyType key{name, std::type_index(typeid(std::tuple<T...>))}; registry()[key] = reinterpret_cast<void*>(func); return true; } }; class Base : public myFactory { public: virtual void f() = 0; virtual ~Base() = default; }; class DerivedExample : public Base { private: // 为每个构造函数注册项定义独立的静态变量,避免重复定义 static bool sign_default; static bool sign_two_int; public: DerivedExample(int a, int b) { std::cout << "Construct with " << a << " and " << b << std::endl; } DerivedExample() = default; static std::shared_ptr<Base> create() { return std::make_shared<DerivedExample>(); } static std::shared_ptr<Base> create(int a, int b) { return std::make_shared<DerivedExample>(a, b); } virtual void f() override { std::cout << "DerivedExample" << std::endl; } }; // 注册无参构造版本 bool DerivedExample::sign_default = DerivedExample::myFactory::sign<decltype(&DerivedExample::create), void>("DerivedExample", &DerivedExample::create); // 注册带两个int参数的版本 bool DerivedExample::sign_two_int = DerivedExample::myFactory::sign<decltype(&DerivedExample::create(int, int)), int, int>("DerivedExample", &DerivedExample::create); int main() { std::shared_ptr<Base> p1 = Base::instantiate("DerivedExample"); std::shared_ptr<Base> p2 = Base::instantiate("DerivedExample", 1, 2); p1->f(); p2->f(); return 0; }
关键改动说明
- 注册表键优化:将原单一字符串键改为
std::pair<std::string, std::type_index>,用std::type_index记录参数类型元组的信息,确保不同参数的构造函数注册到独立条目,不会互相覆盖。 - 解决静态变量冲突:为每个注册的构造函数定义独立的静态变量(
sign_default和sign_two_int),避免重复定义编译错误。 - 完美转发参数:在
instantiate函数中使用std::forward<T>(args)...,确保参数传递的正确性,尤其是处理右值引用场景。 - 明确注册类型:调用
sign时显式指定模板参数,让工厂能正确生成对应参数类型的键。
简化替代方案
如果不需要自动识别参数类型,也可以直接用不同的注册名区分构造函数,实现更简单:
// 注册时使用不同的名字 bool DerivedExample::sign_default = DerivedExample::myFactory::sign("DerivedExample", &DerivedExample::create); bool DerivedExample::sign_two_int = DerivedExample::myFactory::sign("DerivedExample_int_int", &DerivedExample::create); // 实例化时对应不同的注册名 std::shared_ptr<Base> p2 = Base::instantiate("DerivedExample_int_int", 1, 2);
这种方式需要手动维护注册名和参数的对应关系,适合简单场景。
内容的提问来源于stack exchange,提问作者user20995624
相关产品推荐
相关产品推荐

