You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++工厂中按类型过滤对象?探讨无实例化实现方案

问题描述

我现有一个AppFactory工厂类,内部通过map存储对象名称与创建函数的映射,核心代码如下:

template<class T>
static T* makeObj()
{
    return new T();
}

using createFunction = std::function<void*()>;
map<std::string, createFunction> types;

template<class T>
static void add(const std::string& name)
{
    types.insert(name, makeObj<T>);
}

现在我要添加一个模板函数getFilteredListOfTypes<BaseType>,用来获取所有继承自BaseType的类名称列表,函数原型如下:

template<class baseType>
static std::list<std::string> getFilteredListOfTypes()
{
}

我现在卡在实现这个函数的思路上:

  • 第一种思路:修改map的存储值为包含创建函数和对应类型实例的结构体,通过dynamic_cast判断类型关系,但担心实例化大量大型对象会带来性能开销;
  • 第二种思路:使用std::is_base_of模板判断继承关系,但该模板需要明确基类和派生类类型,我能从函数参数获取基类类型,但无法获取已注册的派生类类型,要是通过创建实例再用decltype获取类型,又回到了实例化的问题。

我想知道:

  1. 是否存在无需创建对象即可实现该过滤函数的方案?
  2. 当前基于实例化的实现方案是否存在弊端?
  3. 当工厂中注册数百个大型类型时,实例化每个类型是否会引发问题?

现有完整实现代码

AppFactory.h

class AppFactory
{
public:
    template<class T>
    static T* makeObj()
    {
        return new T();
    }

    using createFunction = std::function<void*()>;
    using registerMap = tsl::robin_map<std::string, createFunction>;

    static registerMap& get();

    template<class T>
    static std::unique_ptr<T> createUnique(const std::string& name)
    {
        return std::unique_ptr<T>(create<T>(name));
    }

    template<class T>
    static std::shared_ptr<T> createShared(const std::string& name)
    {
        return std::shared_ptr<T>(create<T>(name));
    }

    template<class T>
    static T* create(const std::string& name)
    {
        if(get().contains(name))
        {
            return static_cast<T*>(get()[name]());
        }
        return nullptr;
    }

    template<class T>
    static bool add(const std::string& name)
    {
        auto resultPair = get().insert_or_assign(name, makeObj<T>);
        return resultPair.second;
    }
};

#define APPFACTORY_ADD(classname) \
namespace { static bool addName = AppFactory::add<classname>(#classname); }

AppFactory.cpp

AppFactory::registerMap& AppFactory::get()
{
    static AppFactory::registerMap map;
    return map;
}

工厂使用示例

...
APPFACTORY_ADD(SomeClass);
...
AppFactory::createUnique<SomeBaseClass>("SomeClass")
...

基于实例化的getFilteredListOfTypes测试实现
struct typedata
{
    createFunction func;
    Object* obj;
};
using registerMap = tsl::robin_map<std::string, typedata>;
...
template<class T>
static bool add(const std::string& name)
{
    typedata data;
    data.func = makeObj<T>;
    data.obj = new T();
    auto resultPair = get().insert_or_assign(name, data);
    return resultPair.second;
}
...
template<class BaseType>
static std::list<std::string> getFilteredListOfTypes()
{
    std::list<std::string> typeList;
    for(auto it = get().begin(); it != get().end(); ++it)
    {
        if(dynamic_cast<BaseType*>(get()[it->first].obj))
        {
            typeList.push_back(it->first);
        }
    }
    return typeList;
}

过滤函数使用示例

class A : public Object
...
class B : public A
...
class C : public B
...
class D : public C
...
class E : public B
...
lst = AppFactory::getFilteredListOfTypes<A>(); // lst -> {"A", "B", "C", "D", "E"}
lst = AppFactory::getFilteredListOfTypes<B>(); // lst -> {"B", "C", "D", "E"}
lst = AppFactory::getFilteredListOfTypes<C>(); // lst -> {"C", "D"}
lst = AppFactory::getFilteredListOfTypes<D>(); // lst -> {"D"}

解答

1. 无需实例化对象的实现方案

有两种可行的无实例化方案,前提是你的基类/派生类是多态类型(包含虚函数,比如虚析构函数):

方案一:利用空指针dynamic_cast特性

C++标准允许对空指针执行dynamic_cast:如果派生类可以安全转换为目标基类,转换会返回非空的空指针;否则返回nullptr。我们可以基于这个特性修改工厂的注册逻辑:

// 修改存储条目,增加获取派生类空指针的函数
struct TypeEntry {
    createFunction creator;
    void* (*getDerivedNullPtr)();
};

using registerMap = tsl::robin_map<std::string, TypeEntry>;

// 辅助函数:返回指定类型的空指针
template<class T>
static void* getNullPtrForType() {
    return static_cast<void*>(static_cast<T*>(nullptr));
}

// 修改注册函数
template<class T>
static bool add(const std::string& name) {
    TypeEntry entry;
    entry.creator = makeObj<T>;
    entry.getDerivedNullPtr = &getNullPtrForType<T>;
    return get().insert_or_assign(name, entry).second;
}

// 过滤函数实现
template<class BaseType>
static std::list<std::string> getFilteredListOfTypes() {
    static_assert(std::is_polymorphic_v<BaseType>, "BaseType must be polymorphic (have virtual functions)");
    std::list<std::string> result;
    for (const auto& [name, entry] : get()) {
        void* derivedNull = entry.getDerivedNullPtr();
        // 尝试将派生类空指针转换为基类指针
        if (dynamic_cast<BaseType*>(derivedNull) != nullptr) {
            result.push_back(name);
        }
    }
    return result;
}

这个方案完全不需要实例化对象,仅通过指针类型转换完成继承关系判断,性能开销极小。

方案二:编译期类型收集(仅适用于静态注册场景)

如果所有类型都是通过APPFACTORY_ADD宏在编译期注册的,可以利用元编程技术将所有注册类型收集到一个编译期列表中,再通过std::is_base_of_v筛选:

// 定义类型列表元编程结构
template<typename... Ts>
struct TypeList {};

// 全局类型列表(需要修改注册宏来填充)
inline auto& globalTypeList() {
    static TypeList<> list;
    return list;
}

// 修改注册宏,将类型添加到全局列表
#define APPFACTORY_ADD(classname) \
namespace { \
    static bool addName = AppFactory::add<classname>(#classname); \
    static auto registerType = [](){ \
        globalTypeList() = TypeList<classname, decltype(globalTypeList())::types...>(); \
        return true; \
    }(); \
}

// 过滤函数通过遍历编译期类型列表实现
template<class BaseType, typename... Ts>
static std::list<std::string> filterTypes(TypeList<Ts...>) {
    std::list<std::string> result;
    ((std::is_base_of_v<BaseType, Ts> ? result.push_back(#Ts) : void()), ...);
    return result;
}

template<class BaseType>
static std::list<std::string> getFilteredListOfTypes() {
    return filterTypes<BaseType>(globalTypeList());
}

这种方案完全在编译期完成筛选,运行时无开销,但仅适用于静态注册的类型,无法支持动态加载的插件类型。

2. 基于实例化的实现方案的弊端

  • 内存浪费:所有注册类型的实例会长期驻留内存,大型对象会快速消耗内存资源。
  • 初始化副作用:对象构造函数可能执行复杂逻辑(如分配资源、连接外部服务),导致程序启动缓慢,甚至引发不必要的副作用。
  • 析构风险:如果实例未被正确析构会导致内存泄漏;若析构函数有副作用,可能影响程序稳定性。
  • 线程安全隐患:多线程环境下注册实例时,若构造函数未做同步,可能引发竞态条件。

3. 数百个大型类型实例化的问题

当注册数百个大型类型时,实例化会带来严重问题:

  • 内存耗尽:单个大型对象可能占用几十MB内存,数百个实例会直接耗尽可用内存,导致程序崩溃或卡顿。
  • 启动延迟:每个对象的初始化逻辑叠加后,程序启动时间会显著延长,严重影响用户体验。
  • 资源竞争:若多个对象构造函数访问共享资源(如数据库、文件),会引发资源竞争,进一步降低性能。
  • 维护成本上升:单个类型的构造函数变更可能影响整个工厂的注册流程,增加维护难度。

内容的提问来源于stack exchange,提问作者Andrii

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 21:57:02