c++ - C++ Singletons:这个解决方案有多好?优点/缺点,替代方案
问题描述
我正在开发一个 C++ 项目,该项目具有多个必须是单例的类,它们之间存在依赖关系(初始化顺序很重要)。
我想出了这个解决方案:
- 我想成为单例的所有类都有受保护的构造函数,例如:
class MySingleton1 { protected: MySingleton1(); }
- 有一个源文件singleton_factory.cpp包含一个实例化的类Singletons,它派生自我想成为 singletons 的所有类,如下所示:
#include "MySingleton1.hpp" #include "MySingleton2.hpp" class Singletons : public MySingleton1, public MySingleton2 {} static Singletons s_singletons;
- 仍然在singleton_factory.cpp中,对于每个单例类型,还实现一个getSingleton函数的特化:
template<> MySingleton1& getSingleton() { return s_singletons; } template<> MySingleton2& getSingleton() { return s_singletons; }
- getSingleton的特化将“隐藏”在通用模板变体下,在singleton_factory.hpp中:
template <class TSingleton> TSingleton& getSingleton();
优点:
低耦合:
- 单例类不需要“了解”单例类,只需将其构造函数隐藏在受保护的限定符下(这甚至不是强制性的,只是一种好的做法)。唯一真正了解 Singletons 类的代码是singleton_factory.cpp
- 具体实例的瘦依赖:想要使用 T 类型的单例的代码只需要包含 T 类型的标头和瘦的singleton_factory.hpp
初始化顺序可以通过改变Singletons类的继承顺序来控制
- 没有延迟初始化=>线程安全?
- getSingleton()很快,没有 dynamic_cast,没有 reinterpret_cast
缺点:
- 每次出现一个新的单例类型时,一个getSingleton 特化,做同样的事情 - 即“ return s_singletons; ”必须添加到singleton_factory.cpp
所以,据我所知,这实际上相当不错,所以我想把它保留下来,但我正在征求您的反馈意见(还有比编程社区更好的地方吗?)。
您认为此解决方案有哪些额外的优势/劣势?
你有什么替代方案?
解决方案
这迫使单例集中化,这可能会弄乱更复杂项目中的依赖关系。拥有的库singleton.cpp
必须依赖于每个单例所需的一切。同时,任何使用单例的人都必须依赖singleton.cpp
库。
基本上你的代码只能在一个整体的非模块化项目中工作。将其扩展到多个动态库几乎是不可能的。
您的初始化顺序必须手动维护。
静态全局变量的构造点与main
.
我使用的一个不错的解决方案是创建一个包含单例内存的动态库。
要成为单例,您需要从提供::Instance()
内联方法的 CRTP 助手继承。想要单身人士使用的人::Instance()
。
::Instance()
创建一个静态局部变量生命周期令牌。然后它会尝试从主 DLL 中为单例获取存储空间;如果对象已经创建,它只是将存储转换为对象类型,并增加其引用计数。
如果没有,它会创建新的存储并在其中构造对象。
在静态局部变量生命周期令牌的销毁时,它会减少引用计数。如果该引用计数达到 0,它将在当前动态库中本地销毁它。
单例的生命周期现在是已::Instance()
创建变量生命周期的并集。破坏发生在非类型擦除代码中,因此我们不必担心代码被卸载的 DLL。存储是中心。存储存储的 DLL 必须比 Singleton 系统的每个用户都低级别,但它又没有依赖关系,所以这并不痛苦。
这远非完美。单例和生命周期是一个持续存在的问题,因为干净的程序关闭是困难的,而且单例的存在也变得更加困难。但到目前为止,它已经在一个相当大的项目中发挥了作用。
推荐阅读
- yolo - 为什么 Yolo 在 xavier 中运行缓慢?
- angular - Angular 9 项目在使用安全的 null 访问和 null 合并时突然抛出编译错误?
- spring-security - 如何让 KeyCloak 给出一个 *integer* id(既不是长 UUID 也不是用户名)?
- angular - Angular 子组件中断表
- python - 列表只接受一条评论
- jquery - 将 JQuery 调整大小功能应用于我容器中的所有不同 DIV-ID
- python - 使用 scipy.io 打开一个 arff 文件
- shader - MRTK2 模板效果不适用于 TextMeshPro 文本
- sql - 在 Postgresql 中存储数组
- node.js - sinon-mongoose 不适用于 sinon 9