当前位置:   article > 正文

C++单例模式模板类_c++唯一实例模板类

c++唯一实例模板类

        在C++的代码中是要尽量避免使用全局变量的,全局变量可能在程序的任一地方被修改,提高代码的定位难度,还会导致代码耦合性变高,难以模块化测试。但有时候一个类对象想要全局使用,且全局只能初始化一次,这时就可以引入单例模式的思想。这里提到的只是单例模式应用的一个场景,实际上全局变量和单例没太大的相关性,单例和静态类的区别在这里不再赘述,网上可以查找到相关的对比,比如下面这个是C#上的说明:

单例模式和静态类 - kerwin cui - 博客园

一、单例模式

        在《Head First 设计模式》一书中,单例模式的定义是“确保一个类只有一个实例,并提供一个全局访问点”,类图如下:

        通过类图可以看到单例类有两个明显的特点:① 构造函数是私有的,这样在类外就不能随意进行实例化;② 有一个静态的函数,用来获取类内实例化的对象,类的静态函数可以在代码的任何地方进行调用。根据单例类的这两个特点,可以设计出两种经典的单例模式:懒汉式和饿汉式,还有基于懒汉式的线程安全的双检测锁模式,在下面这篇文章中有很详细的说明:

C++ 单例模式 - 知乎

二、双检测锁模式

        为了在多线程下使用单例模式,基于线程安全问题仅出现在第一次初始化(new)过程中,引入了双检锁模式。第一次检测不涉及到对象的创建,因此是不加锁的检测,不需要锁的时间消耗,只有检测到对象暂未创建的时候,才会进行对象的加锁操作,然后再进行数据的第二次检测,避免在加锁期间,对象已经创建成功,只有二次检测到对象尚未创建才会创建唯一的实例。

        为了在大规模的代码中,方便多个类都定义为单例类,且在写类的代码时不需要太大变动且模式统一,因此本文定义了一个模板类,通过可变模板参数和友元函数的概念,实现调用的统一,具体实现代码如下:

  1. template <typename T>
  2. class Singleton {
  3. public:
  4. template<typename... Args>
  5. static inline std::shared_ptr<T> instance(Args&& ... args) {
  6. // 双检测锁模式
  7. if (!instance_.get()) {
  8. std::unique_lock<std::mutex> lock(instanceMutex_);
  9. if (!instance_.get()) {
  10. instance_.reset(new T(std::forward<Args>(args)...));
  11. }
  12. }
  13. return instance_;
  14. }
  15. private:
  16. Singleton() = default;
  17. virtual ~Singleton() = default;
  18. Singleton(const Singleton&) = default;
  19. Singleton& operator = (const Singleton&) = delete;
  20. // 实例
  21. static std::shared_ptr<T> instance_;
  22. static std::mutex instanceMutex_;
  23. };
  24. template <typename T>
  25. std::shared_ptr<T> Singleton<T>::instance_;
  26. template <typename T>
  27. std::mutex Singleton<T>::instanceMutex_;
  28. #define SINGLETON_DECL(type) \
  29. friend class std::shared_ptr< type >; \
  30. friend class Singleton< type >;

        双检锁模式可以在多线程下保证线程的安全,但并不是绝对的。某些内存模型、编译器的优化或者运行时优化等情况下,会先分配完内存再进行数据的构造,造成另一个线程如果调用getInstance()获取到一个不完全初始化的对象,从而出现崩溃的情况。这种情况一个是通过内存屏障(memory barrier)的方式解决,一个是在主函数或者是比较早期的时候就完成单例的创建,还有一个方式是atomic实现,可以参考第一节中《C++ 单例模式》链接里面讲解的很清晰,这里不再赘述,只提供一种单例模板类的实现思路。

三、单例模板类的调用

        将单例模板类定义在头文件Singleton.h中,其它头文件中想要定义为单例模式的类,只需要对其进行引用(#include Singleton.h)即可进行使用,假设我们想定义一个单例类A,则可以通过以下代码实现:

  1. class A{
  2. private:
  3. A() {}
  4. public:
  5. ~A() {}
  6. int funA() {}
  7. private:
  8. SINGLETON_DECL(A);
  9. };
  10. // 调用单例类A的公共成员函数
  11. Singleton<A>::instance()->funA();
  12. // 在调用的时候会进行实例是否已创建的检查

        可以看到调用是比较简单且统一的,不需要针对每个类再单独写双检测锁的部分。单例模式的实现方式有很多,可以根据项目实际需求选择具体方式,不管是懒汉式、饿汉式还是优化实现,都可以通过可变参数模板进行全局的统一管理。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/137772
推荐阅读
相关标签
  

闽ICP备14008679号