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

如何实现每个对象实例独立的Per-object singleton模式?

实现方案

你需要的是每个O实例内部维护类型到对应唯一实例的映射,平均O(1)时间复杂度的实现可以通过std::type_index作为键的哈希表完成,以下提供两种常用实现:


方案1:需要基类(兼容C++11及以上版本)

首先定义公共基类,需要虚析构函数保证资源正确释放:

#include <unordered_map>
#include <memory>
#include <typeindex>
#include <type_traits>

class TBase {
public:
    virtual ~TBase() = default;
};

然后实现O类:

class O {
private:
    std::unordered_map<std::type_index, std::unique_ptr<TBase>> m_instanceMap;

public:
    template<class T>
    T& getSingleton() {
        // 编译期检查T是否继承自TBase
        static_assert(std::is_base_of<TBase, T>::value, "T must inherit from TBase");
        
        const auto typeKey = std::type_index(typeid(T));
        auto it = m_instanceMap.find(typeKey);
        if (it == m_instanceMap.end()) {
            // 类型不存在则创建新实例存入
            auto [newIt, _] = m_instanceMap.emplace(typeKey, std::make_unique<T>());
            it = newIt;
        }
        // 这里转换必然成功,无需额外判空
        return *dynamic_cast<T*>(it->second.get());
    }

    // 如果需要支持带参数构造的T,可以用这个重载
    template<class T, class... Args>
    T& getSingleton(Args&&... args) {
        static_assert(std::is_base_of<TBase, T>::value, "T must inherit from TBase");
        
        const auto typeKey = std::type_index(typeid(T));
        auto it = m_instanceMap.find(typeKey);
        if (it == m_instanceMap.end()) {
            auto [newIt, _] = m_instanceMap.emplace(typeKey, std::make_unique<T>(std::forward<Args>(args)...));
            it = newIt;
        }
        return *dynamic_cast<T*>(it->second.get());
    }
};

方案2:无基类要求(兼容C++17及以上版本)

用C++17引入的std::any做类型擦除,不需要提前定义公共基类,开放度更高:

#include <unordered_map>
#include <any>
#include <typeindex>
#include <utility>

class O {
private:
    std::unordered_map<std::type_index, std::any> m_instanceMap;

public:
    template<class T>
    T& getSingleton() {
        const auto typeKey = std::type_index(typeid(T));
        auto it = m_instanceMap.find(typeKey);
        if (it == m_instanceMap.end()) {
            auto [newIt, _] = m_instanceMap.emplace(typeKey, T());
            it = newIt;
        }
        return *std::any_cast<T>(&it->second);
    }

    // 带参数构造的重载
    template<class T, class... Args>
    T& getSingleton(Args&&... args) {
        const auto typeKey = std::type_index(typeid(T));
        auto it = m_instanceMap.find(typeKey);
        if (it == m_instanceMap.end()) {
            auto [newIt, _] = m_instanceMap.emplace(typeKey, T(std::forward<Args>(args)...));
            it = newIt;
        }
        return *std::any_cast<T>(&it->second);
    }
};

注意事项

  • 多线程场景下需要在getSingleton方法入口加互斥锁,避免并发读写哈希表的异常:
    #include <mutex>
    // O类增加成员
    std::mutex m_mutex;
    // 函数开头加锁
    std::lock_guard<std::mutex> lock(m_mutex);
    
  • 两种方案的平均查找/插入复杂度都是O(1),远优于线性遍历+dynamic_cast的方案,关联的类型越多性能优势越明显。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.17 11:54:07