首页 > 编程语言 >c++单例模式总结

c++单例模式总结

时间:2023-09-03 20:13:58浏览次数:30  
标签:总结 std Singleton const c++ ins 线程 单例 delete

分类

懒汉式:实例对象在第一次被使用时才进行初始化。

饿汉式:实例在定义时就被初始化。

特点

1、构造函数和析构函数私有化,不允许外部创建实例对象。

2、拷贝构造函数和复制运算符重载被delete,不允许产生新的实例。

3、内部定义一个私有的静态数据成员,该成员为本类的实例化对象。

4、提供公有静态成员方法获取该静态对象。

懒汉式

单线程懒汉式实现

 1 class Singleton
 2 {
 3 public:
 4     static Singleton* Ins()
 5     {
 6         if (_ins == nullptr)
 7             _ins = new Singleton();
 8         return _ins;
 9     }
10 
11 protected:
12     Singleton() {}
13     ~Singleton() {}
14     Singleton(const Singleton&) = delete;
15     Singleton& operator=(const Singleton&) = delete;
16 
17 private:
18     static Singleton* _ins;
19 };

缺陷:

  • 非线程安全:假设目前有2个线程同时访问Singleton::Ins方法,线程A在if条件判断为真后,失去时间片,此时_ins还未初始化。线程B访问Singleton::Ins,由于在A线程中还未初始化_ins,导致B线程创建对象并将_ins初始化完成。此时,时间片再次回到A线程,还原现场,上一个时间片中已经判断过if条件,_ins将调用new创建新对象实例,导致对象被创建两次,内存泄漏。
  • 内存泄漏:在类中,我们只负责new了一个对象,并未对其delete,导致内存泄漏。

下面将用双重锁+智能指针解决上面缺陷。

双重锁+智能指针实现

 1 class Singleton
 2 {
 3 public:
 4     ~Singleton() { std::cout << "destructor" << std::endl; }    //必须声明为public
 5 
 6     static std::shared_ptr<Singleton> Ins()
 7     {
 8         if (_ins == nullptr)
 9         {
10             std::lock_guard<std::mutex> lock(_mt);
11             if (_ins == nullptr)
12             {
13                 _ins = std::shared_ptr<Singleton>(new Singleton());
14             }
15         }
16         return _ins;
17     }
18 
19     Singleton(const Singleton&) = delete;
20     Singleton& operator=(const Singleton&) = delete;
21 
22 protected:
23     Singleton() { std::cout << "constructor" << std::endl; }
24 
25 private:
26     static std::shared_ptr<Singleton> _ins;
27     static std::mutex _mt;
28 };

缺点:

  • 双重锁在特定环境下依旧是非线程安全的。
  • 强制用户使用智能指针,并且要求将析构函数定义为piblic,可能在实际使用中忽略了该问题导致程序编译出错。

局部静态变量法(推荐)

 1 class Singleton
 2 {
 3 public:
 4     static Singleton& Ins()
 5     {
 6         static Singleton _ins;
 7         return _ins;
 8     }
 9 
10     Singleton(const Singleton&) = delete;
11     Singleton& operator=(const Singleton&) = delete;
12 
13 protected:
14     Singleton() { std::cout << "constructor" << std::endl; }
15     ~Singleton() { std::cout << "destructor" << std::endl; }
16 };

C++11标准中定义了一个Magic Static特性:如果变量当前处于初始化状态,当发生并发访问时,并发线程将会阻塞,等待初始化结束。

std::call_once + 内部类方法

 1 class Singleton
 2 {
 3 public:
 4     static Singleton* Ins()
 5     {
 6         std::call_once(_flag, []() {
 7             _ins = new Singleton;
 8         });
 9         return _ins;
10     }
11 
12     Singleton(const Singleton&) = delete;
13     Singleton& operator=(const Singleton&) = delete;
14 
15 protected:
16     Singleton() { std::cout << "constructor" << std::endl; }
17     ~Singleton() { std::cout << "destructor" << std::endl; }    //必须声明为私有,否则返回指针将可析构
18 
19 private:
20     struct Deleter
21     {
22         ~Deleter() {
23             delete _ins;
24             _ins = nullptr;
25         }
26     };
27     static Deleter _deleter;
28     static Singleton* _ins;
29     static std::once_flag _flag;
30 };
31 
32 Singleton::Deleter Singleton::_deleter;
33 Singleton* Singleton::_ins = nullptr;
34 std::once_flag Singleton::_flag;

缺点:不美观,在类内部定义内部类,头文件臃肿,适用于.h和.cpp分离的情况下使用。

饿汉式

 

 1 class Singleton
 2 {
 3 public:
 4     static Singleton& Ins()
 5     {
 6         return _ins;
 7     }
 8 
 9     Singleton(const Singleton&) = delete;
10     Singleton& operator=(const Singleton&) = delete;
11 
12 protected:
13     Singleton() { std::cout << "constructor" << std::endl; }
14     ~Singleton() { std::cout << "destructor" << std::endl; }
15 
16 private:
17     static Singleton _ins;
18 };
19 
20 Singleton Singleton::_ins;

模板单例

主要解决系统中存在多个模块需要使用单例模式,通过模板+继承解决代码冗余。

 1 template<typename T>
 2 class Singleton
 3 {
 4 public:
 5     static T& Ins()
 6     {
 7         static T _ins;
 8         return _ins;
 9     }
10 
11 protected:
12     Singleton() { std::cout << "constructor" << std::endl; }
13     ~Singleton() { std::cout << "destructor" << std::endl; }
14     Singleton(const Singleton&) = delete;
15     Singleton& operator=(const Singleton&) = delete;
16 };
17 
18 class AppInstance : public Singleton<AppInstance>
19 {
20     friend Singleton<AppInstance>;        //声明友元
21     AppInstance() {}    //必须私有化
22     ~AppInstance() {}
23 public:
24     void func() { std::cout << "func"<< std::endl; }
25 };

总结

除非频繁访问或者频繁创建和销毁,尽量不要使用单例模式,可以用组合的方式代替。

在使用单例模式,需要确保线程安全、内存安全。设计时,尽量做到好用、简单。

 

标签:总结,std,Singleton,const,c++,ins,线程,单例,delete
From: https://www.cnblogs.com/BroccoliFighter/p/17675445.html

相关文章

  • uniapp项目实践总结(八)自定义加载组件
    有时候一个页面请求接口需要加载很长时间,这时候就需要一个加载页面来告知用户内容正在请求加载中,下面就写一个简单的自定义加载组件。目录准备工作逻辑思路实战演练效果预览准备工作在之前的全局组件目录components下新建一个组件文件夹,命名为q-loading,组件为q-loading......
  • c++11关键字
    decltype关键字:查询关键字的数据类型#语法:decltype(expression)var;1)如果expression是没有用括号括起来的标识符(不包括decltype本身的括号,则var的类型与该标识符的类型相同,包括const等限定符,注意如果返回值为引用时需要先初始化2)如果expression是函数调用,则var的类型与......
  • Google C++编程规范(Google C++ Style Guide)
    参考链接:Google代码规范C++总结Google开源项目风格指南——中文版GoogleC++StyleGuide是一份不错的C++编码指南,我制作了一张比较全面的说明图,可以在短时间内快速掌握规范的重点内容。不过规范毕竟是人定的,记得活学活用。看图前别忘了阅读下面两条重要建议:保持一致也......
  • C/C++ const关键字 解读
    Thecollocationbetweenconstandoriginalpointerisconfusedtomanypeople.Therearetwousagesofit.Thefirstoneisavariablepointerthatpointsaconstantdata.i.e.constint*p#include<iostream>intmain(){ inta=1,b=2; const......
  • java面向对象高级(根据青空的霞光总结)
    #面向对象高级(青空)基本类型包装类前置:虽然java是面向对象的语言,但是基本类型不是面向对象的,如果想要让基本类型也能像面向对象的形式进行表达,就可以是用包装类包装类实际上就是将我们的基本数据类型,封装成一个类(运用了封装的思想)类型:byte->Byteboolean->Booleans......
  • 排序算法性能总结(时间复杂度)
    学习:https://blog.csdn.net/weixin_43207025/article/details/114902065......
  • C++算法之旅、05 基础篇 | 第二章 数据结构
    常用代码模板2——数据结构-AcWing笔试用数组模拟而不是结构体使用结构体指针,newNode()非常慢,创建10万个节点就超时了,做笔试题不会用这种方式(优化是提前初始化好数组,但这样跟数组模拟没区别了,而且代码量很长)单链表(数组)使用两个数组,e存储val,ne存储next。空节点next用-1表......
  • 《C++并发编程实战》读书笔记(2):线程间共享数据
    1、使用互斥量在C++中,我们通过构造std::mutex的实例来创建互斥量,调用成员函数lock()对其加锁,调用unlock()解锁。但通常更推荐的做法是使用标准库提供的类模板std::lock_guard<>,它针对互斥量实现了RAII手法:在构造时给互斥量加锁,析构时解锁。两个类都在头文件<mutex>里声明。std::......
  • 计算机网络总结
    计算机网络笔记简书1......
  • javaee spring注解设置单例模式和懒加载模式
    @Lazy懒加载@Scope(scopeName=“prototype”)设置多例模式,不加默认单例模式@Lazy@Component@Scope(scopeName="prototype")publicclassDrink{@Value("橙汁")privateStringname;@Value("半糖")privateStringsugar;@Value(&quo......