如何实现每个对象实例独立的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
相关产品推荐
相关产品推荐

