分类

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

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

特点

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 };

总结

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

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

c++单例模式总结的更多相关文章

  1. C++实现线程安全的单例模式

    在某些应用环境下面,一个类只允许有一个实例,这就是著名的单例模式.单例模式分为懒汉模式,跟饿汉模式两种. 首先给出饿汉模式的实现 template <class T> class sing ...

  2. 23种设计模式--单例模式-Singleton

    一.单例模式的介绍 单例模式简单说就是掌握系统的至高点,在程序中只实例化一次,这样就是单例模式,在系统比如说你是该系统的登录的第多少人,还有数据库的连接池等地方会使用,单例模式是最简单,最常用的模式之 ...

  3. angular2系列教程(十)两种启动方法、两个路由服务、引用类型和单例模式的妙用

    今天我们要讲的是ng2的路由系统. 例子

  4. java设计模式之--单例模式

    前言:最近看完<java多线程编程核心技术>一书后,对第六章的单例模式和多线程这章颇有兴趣,我知道我看完书还是记不住多少的,写篇博客记录自己所学的只是还是很有必要的,学习贵在坚持. 单例模 ...

  5. 设计模式C#合集--单例模式

    单例模式 代码: 第一种: private static Singleton singleton = null; private Singleton() { } public static Singl ...

  6. 设计模式之单例模式(Singleton)

    设计模式之单例模式(Singleton) 设计模式是前辈的一些经验总结之后的精髓,学习设计模式可以针对不同的问题给出更加优雅的解答 单例模式可分为俩种:懒汉模式和饿汉模式.俩种模式分别有不同的优势和缺 ...

  7. GOF23设计模式之单例模式

    ·核心作用: -保证一个类只有一个实例,并且提供一个访问该实例的全局访问点. ·常见应用场景: -Windows的Task Manager(任务管理器)就是很典型的单例模式 -Windows的Recy ...

  8. GJM : C#设计模式(1)——单例模式

    感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...

  9. PHP设计模式(四)单例模式(Singleton For PHP)

    今天讲单例设计模式,这种设计模式和工厂模式一样,用的非常非常多,同时单例模式比较容易的一种设计模式. 一.什么是单例设计模式 单例模式,也叫单子模式,是一种常用的软件设计模式.在应用这个模式时,单例对 ...

  10. java设计模式之单例模式(几种写法及比较)

    概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...

随机推荐

  1. 为 Typecho 添加 B 站蛆音娘表情

    首先为什么帮助文档写的很清楚了我还要发,我只是让你们少走弯路一次弄好,后面我会说到 #1. 准备并上传表情文件 首先,你需要在文章底部下载表情文件,对于表情文件,有如下要求和建议: 所有后缀名必须相同 ...

  2. 启动U盘制作-小白保姆式超详细刷机教程

    疑难解答加微信机器人,给它发:进群,会拉你进入八米交流群 机器人微信号:bamibot 简洁版教程访问:https://bbs.8miyun.cn 一.准备工作 需要用到的工具: 1.一台Window ...

  3. 错误模块名称:vrfcore.dll

    记录一下. 应用程序莫名报这个错,其它电脑上正常. 可能是Application Verifier这个工具影响到了. 进入注册表:win+R->regedit->HKEY_LOCAL_MA ...

  4. 重生之数据结构与算法----数组&链表

    简介 数据结构的本质,只有两种结构,数组与链表.其它的都是它的衍生与组合 算法的本质就是穷举. 数组 数组可以分为两大类,静态数组与动态数组. 静态数组的本质是一段连续的内存,因为是连续的,所以我们可 ...

  5. docker中 启动所有的容器命令

    docker中 启动所有的容器命令 docker start $(docker ps -a | awk '{ print $1}' | tail -n +2) docker中    关闭所有的容器命令 ...

  6. 推荐8款 .NET 开源、免费、实用的 Windows 效率软件

    前言 今天大姚给大家推荐8款基于 .NET 开源.免费.实用的 Windows 效率软件,开发工作提升利器,希望可以帮助到有需要的小伙伴. DevToys DevToys是一个专门为开发者设计的Win ...

  7. 继承内存图--java进阶 day01

    主方法进栈,有new进堆 堆内存中先存自己类中有的变量 又因为继承了父类,所以父类中的变量也要存入 即使被私有化,依旧可以继承,只是没有权限使用! 创建对象时,会调用构造方法,所以走构造方法,实参传形 ...

  8. 如果在安装32位Oracle客户端组件的情况下64位模式运行, 将出现此问题.

    场景重现 在一台Windows 7 32-bit电脑上 安装了Oracle 11gR2 32-bit的客户端 用 VS2010 写的一个基于数据库驱动的项目 操作Oracle数据库都挺正常的 后来.. ...

  9. C#之json字符串转xml字符串

    留爪参考 using System.Xml; // using System.Text; // using System.Runtime.Serialization.Json; //JsonReade ...

  10. SQL SERVER日常运维巡检系列之-实例级参数

    前言 做好日常巡检是数据库管理和维护的重要步骤,而且需要对每次巡检日期.结果进行登记,同时可能需要出一份巡检报告. 本系列旨在解决一些常见的困扰: 不知道巡检哪些东西不知道怎么样便捷体检机器太多体检麻 ...