C++ 单例模式的几种实现研究
都是从网上学得,整理下自己的理解。
单例模式有两种实现模式:
1)懒汉模式: 就是说当你第一次使用时才创建一个唯一的实例对象,从而实现延迟加载的效果。
2)饿汉模式: 就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。
所以,从实现手法上看, 懒汉模式是在第一次使用单例对象时才完成初始化工作。因为此时可能存在多线程竞态环境,如不加锁限制会导致重复构造或构造不完全问题。
饿汉模式则是利用外部变量,在进入程序入口函数之前就完成单例对象的初始化工作,此时是单线程所以不会存在多线程的竞态环境,故而无需加锁。
以下是典型的几种实现
一、 懒汉模式,标准的 ”双检锁“ + ”自动回收“ 实现
class Singleton
{
public:
static Singleton* GetInstance()
{
if (m_pInstance == NULL )
{
Lock(); // 加锁
if (m_pInstance == NULL )
{
m_pInstance = new Singleton ();
}
UnLock(); // 解锁
}
return m_pInstance;
} // 实现一个内嵌垃圾回收类
class CGarbo
{
public:
~CGarbo()
{
if(Singleton::m_pInstance)
delete Singleton::m_pInstance;
}
}; static CGarbo Garbo; // 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象 private:
Singleton(){};
Singleton(Singleton const&);
Singleton& operator=(Singleton const&); static Singleton* m_pInstance;
}; Singleton* Singleton::m_pInstance = NULL;
Singleton::CGarbo Garbo;
二、静态局部变量的懒汉模式 ,而不是new在堆上创建对象,避免自己回收资源。
这里仍然要注意的是局部变量初始化的线程安全性问题,在C++0X以后,要求编译器保证静态变量初始化的线程安全性,可以不加锁。但C++ 0X以前,仍需要加锁。
class Singleton
{
public:
static Singleton* GetInstance()
{
Lock(); // not needed after C++0x
static Singleton instance;
UnLock(); // not needed after C++0x return &instance;
} private:
Singleton() {};
Singleton(const Singleton &);
Singleton & operator = (const Singleton &);
};
在懒汉模式里,如果大量并发线程获取单例对象,在进行频繁加锁解锁操作时,必然导致效率低下。
三、饿汉模式,基础版本
因为程序一开始就完成了单例对象的初始化,所以后续不再需要考虑多线程安全性问题,就可以避免懒汉模式里频繁加锁解锁带来的开销。
class Singleton
{
public: static Singleton* GetInstance()
{
return &m_instance;
} private:
Singleton(){};
Singleton(Singleton const&);
Singleton& operator=(Singleton const&); static Singleton m_instance;
}; Singleton Singleton::m_instance; // 在程序入口之前就完成单例对象的初始化
虽然这种实现在一定程度下能良好工作,但是在某些情况下会带来问题 --- 就是在C++中 ”非局部静态对象“ 的 ”初始化“ 顺序 的 ”不确定性“, 参见Effective c++ 条款47。
考虑: 如果有两个这样的单例类,将分别生成单例对象A, 单例对象B. 它们分别定义在不同的编译单元(cpp中), 而A的初始化依赖于B 【 即A的构造函数中要调用B::GetInstance() ,而此时B::m_instance 可能还未初始化,显然调用结果就是非法的 】, 所以说只有B在A之前完成初始化程序才能正确运行,而这种跨编译单元的初始化顺序编译器是无法保证的。
四、饿汉模式,增强版本(boost实现)
在前面的方案中:饿汉模式中,使用到了类静态成员变量,但是遇到了初始化顺序的问题; 懒汉模式中,使用到了静态局部变量,但是存在着线程安全等问题。
boost 的实现方式是:单例对象作为静态局部变量,然后增加一个辅助类,并声明一个该辅助类的类静态成员变量,在该辅助类的构造函数中,初始化单例对象。以下为代码
class Singleton
{
public:
static Singleton* GetInstance()
{
static Singleton instance;
return &instance;
} protected:
// 辅助代理类
struct Object_Creator
{
Object_Creator()
{
Singleton::GetInstance();
}
};
static Object_Creator _object_creator; Singleton() {}
~Singleton() {}
}; Singleton::Object_Creator Singleton::_object_creator;
首先,代理类这个外部变量初始化时,在其构造函数内部调用 Singleton::GetInstance();从而间接完成单例对象的初始化,这就通过该代理类实现了饿汉模式的特性。
其次,仍然考虑第三种模式的缺陷。 当A的初始化依赖于B, 【 即A的构造函数中要调用B::GetInstance() ,而此时B::m_instance 可能还未初始化,显然调用结果就是非法的 】 现在就变为【在A的构造函数中要调用B::GetInstance() ,如果B尚未初始化,就会引发B的初始化】,所以在不同编译单元内全局变量的初始化顺序不定的问题就随之解决。
最后,关于使用懒汉还是饿汉模式,我的理解:
如果这个单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,也是一种资源浪费吧。 所以这种情况懒汉模式(延迟加载)更好。
如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。
C++ 单例模式的几种实现研究的更多相关文章
- Python 单例模式的几种实现方式
单例模式的几种实现方式 先来看几个魔法方法的简单运用:__new__, __init__, __call__. class A(object): def __init__(self, x): prin ...
- java设计模式之单例模式(几种写法及比较)
概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...
- java单例模式的几种写法比较
概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...
- JAVA中单例模式的几种实现方式
1 线程不安全的实现方法 首先介绍java中最基本的单例模式实现方式,我们可以在一些初级的java书中看到.这种实现方法不是线程安全的,所以在项目实践中如果涉及到线程安全就不会使用这种方式.但是如果不 ...
- Python中的单例模式的几种实现方式的优缺点及优化
单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...
- python实现单例模式的三种方式及相关知识解释
python实现单例模式的三种方式及相关知识解释 模块模式 装饰器模式 父类重写new继承 单例模式作为最常用的设计模式,在面试中很可能遇到要求手写.从最近的学习python的经验而言,singlet ...
- day29单例模式的4种实现模式
单例模式的四种实现模式单例模式实现方式一: import settings class MySQL: __instance=None def __init__(self, ip, port): ...
- Python中的单例模式的几种实现方式的及优化
单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...
- Java设计模式之单例模式(七种写法)
Java设计模式之单例模式(七种写法) 第一种,懒汉式,lazy初始化,线程不安全,多线程中无法工作: public class Singleton { private static Singleto ...
随机推荐
- SpringMVC+MyBatis 返回时间格式转换的解决方案
Spring MVC 4.X ResponseBody 日期类型Json 处理 摘自http://tramp-zzy.iteye.com/blog/2090330 2014-07-10 方法一:全局 ...
- 【网络】RFC1245-OSPF Protocol Analysis
OSPF协议分析 摘要 这是OSPF协议的两份报告中的第一份,这些报告是因特网工程指导组要求的,是用来将一个因特网协议写成标准草案的.OSPF是一个TCP/IP协议族中的一个的路由协议,被设计用于一个 ...
- 51Nod 1009 数字1的个数 | 数位DP
题意: 小于等于n的所有数中1的出现次数 分析: 数位DP 预处理dp[i][j]存 从1~以j开头的i位数中有几个1,那么转移方程为: if(j == 1) dp[i][j] = dp[i-1][9 ...
- SSH ERROR: Too many Authentication Failures
来自: How to recover from "Too many Authentication Failures for user root" 其中一种可以解决的方式 eval ...
- Spring Session加Redis(山东数漫江湖)
session是一个非常常见的概念.session的作用是为了辅助http协议,因为http是本身是一个无状态协议.为了记录用户的状态,session机制就应运而生了.同时session也是一个非常老 ...
- 大聊Python----SocketServer
什么是SocketServer? SocketServer的最主要的作用是实现并发处理,也就是可以多个用户同时上传和下载文件. socketserver模块简化了编写网络服务器的任务. sockets ...
- 【Android XML】Android XML 转 Java Code 系列之 Selector(2)
今天我们要把drawable下的selector的XML文件转换成Java代码.(打包进jar,不依赖apk) 在转换工具中的代码为: https://github.com/SickWorm/Andr ...
- 【Educationcal Codeforces Round 21】
这场edu我原本以为能清真一点…… 后来发现不仅是七题 还有各种奇奇怪怪的骚操作…… A. 随便枚举 #include<bits/stdc++.h> using namespace std ...
- 2017中国大学生程序设计竞赛 - 网络选拔赛 HDU 6152 Friend-Graph 暴暴暴暴力
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6152 题意:判定一个无向图是否有三个点的团或者三个点的独立集. 解法:Ramsey theorem,n ...
- Rsync文件同步服务
Rsync简介 Rsync是一款开源的.快速的.多功能的.可实现全量及增量的本地或远程数据同步备份的优秀工具,适用于Unix/Linux/Windows等多种操作系统. Rsync的特性 支持拷贝特殊 ...