C++ 安全单例模式总结
前两天,一个C++ 的单例实现又掉坑里了。做好一个安全的单例模式可并不简单。这里总结一下C++ 的几个单例实现方案。
1. 函数静态变量法
利用单例函数的静态变量,实现单例构造。代码如下:
class StaticVarSingleTon {
public:
static StaticVarSingleTon *GetInstance() {
static StaticVarSingleTon s_instance;
return &s_instance;
}
private:
StaticVarSingleTon() {}
virtual ~StaticVarSingleTon() {};
StaticVarSingleTon(const StaticVarSingleTon &);
StaticVarSingleTon& operator=(const StaticVarSingleTon& other);
};
这里利用函数的静态变量,只会存在一份的特性,来实现单例的构造。代码直接明了。
优点
- 代码简单,直接明了
- 还是个懒加载模式
缺点
- 静态变量的构造,不是线程安全的。
2. 类静态成员变量
利用类的静态变量的全局唯一性,来实现单例的构造。代码如下:
//
// StaticMemberSingleton.h
//
#ifndef StaticMemberSingleton_h
#define StaticMemberSingleton_h
class StaticMemberSingleTon {
public:
static StaticMemberSingleTon *GetInstance() {
return &s_instance;
}
private:
StaticMemberSingleTon() {}
virtual ~StaticMemberSingleTon() {};
StaticMemberSingleTon(const StaticMemberSingleTon &);
StaticMemberSingleTon& operator=(const StaticMemberSingleTon& other);
private:
static StaticMemberSingleTon s_instance;
};
#endif /* StaticMemberSingleton_h*/
//
// StaticMemberSingleton.cpp
//
#include "StaticMemberSingleton.h"
StaticMemberSingleTon StaticMemberSingleTon::s_instance;
优点
- 这里的StaticMemberSingleTon StaticMemberSingleTon::s_instance 是一个全局变量。只会出现一份。
- 全局变量的初始化,在main 函数执行之前完成。可以保证线程安全。
缺点
- 当有另外一个 StaticMemberSingletonB,在构造函数中依赖 StaticMemberSingletonA 的单例对象时,可能出现StaticMemberSingletonA 的单例对象还没有初始化的问题。
让我们用代码来验证一下,我们构造两个单例:StaticMemberSingletonA, StaticMemberSingletonB.
StaticMemberSingletonA 的构造函数,调用StaticMemberSingletonB 的方法;
StaticMemberSingletonB 的构造函数,调用StaticMemberSingletonA 的方法。
代码如下。
//
// StaticMemberSingletonA.h
//
#ifndef StaticMemberSingletonA_h
#define StaticMemberSingletonA_h
#include <stdio.h>
class StaticMemberSingleTonA {
public:
static StaticMemberSingleTonA *GetInstance() {
return &s_instance;
}
void showValue() {
printf("SingleTonA value %d\n", value);
}
private:
StaticMemberSingleTonA();
virtual ~StaticMemberSingleTonA() {};
StaticMemberSingleTonA(const StaticMemberSingleTonA &);
StaticMemberSingleTonA& operator=(const StaticMemberSingleTonA& other);
private:
static StaticMemberSingleTonA s_instance;
private:
int value = 0;
};
#endif /* StaticMemberSingletonA_h*/
//
// StaticMemberSingletonA.cpp
//
#include "StaticMemberSingletonA.h"
#include "StaticMemberSingletonB.h"
StaticMemberSingleTonA StaticMemberSingleTonA::s_instance;
StaticMemberSingleTonA::StaticMemberSingleTonA() {
value = 1;
StaticMemberSingleTonB::GetInstance()->showValue();
}
//
// StaticMemberSingletonB.h
//
#ifndef StaticMemberSingletonB_h
#define StaticMemberSingletonB_h
#include <stdio.h>
class StaticMemberSingleTonB {
public:
static StaticMemberSingleTonB *GetInstance() {
return &s_instance;
}
void showValue() {
printf("SingleTonB value %d\n", value);
}
private:
StaticMemberSingleTonB();
virtual ~StaticMemberSingleTonB() {};
StaticMemberSingleTonB(const StaticMemberSingleTonB &);
StaticMemberSingleTonB& operator=(const StaticMemberSingleTonB& other);
private:
static StaticMemberSingleTonB s_instance;
private:
int value = 0;
};
#endif /* StaticMemberSingletonB_h*/
//
// StaticMemberSingletonB.cpp
//
#include "StaticMemberSingletonB.h"
#include "StaticMemberSingletonA.h"
StaticMemberSingleTonB StaticMemberSingleTonB::s_instance;
StaticMemberSingleTonB::StaticMemberSingleTonB() {
value = 2;
StaticMemberSingleTonA::GetInstance()->showValue();
}
//
// main.cpp
//
#include <stdio.h>
#include "StaticMemberSingletonA.h"
int main(int argc, const char * argv[]) {
StaticMemberSingleTonA::GetInstance();
return 0;
}
执行一下,结果如下:
SingleTonA value 0
SingleTonB value 2
Program ended with exit code: 0
3. 线程安全的单例方法
一般常见的C++ 线程安全的单例实现代码,如下:
//
// SafeSingleton.h
//
#ifndef SafeSingleton_h
#define SafeSingleton_h
#include "Mutex.h"
class SafeSingleton {
public:
static SafeSingleton *GetInstance();
private:
SafeSingleton() {};
virtual ~SafeSingleton() {};
SafeSingleton(const SafeSingleton &);
SafeSingleton& operator=(const SafeSingleton& other);
private:
static SafeSingleton *s_instance;
static Mutex s_insMutex;
};
#endif /* SafeSingleton_h*/
//
// SafeSingleton.cpp
//
#include "SafeSingleton.h"
SafeSingleton *SafeSingleton::s_instance;
Mutex SafeSingleton::s_insMutex;
SafeSingleton *SafeSingleton::GetInstance() {
if (s_instance == nullptr) {
s_insMutex.lock();
if (s_instance == nullptr) {
s_instance = new SafeSingleton();
}
s_insMutex.unlock();
}
return s_instance;
}
注意:
- 第一次判断 s_instance 非空,是为了提升性能,避免无谓的加锁。
- 获得锁后,必须再次判断 s_instance 非空,避免多线程下二次创建。
- 另外,由于所有实例的构造,都在main函数之后执行了。而锁对象是全局变量,在main 之前就已经完成初始化了,不会出现方案2 中的对象未初始化现象。
- 当然,如果真这儿做了,会出现死锁。
4. 还未结束
我们在C++ 层实现了一个网络状态监控模块,这个模块给iOS 业务层使用。当时业务层实现了自己的一个网络状态模块。大致代码如下所示:
@implementation IOSNetworkState
+ (void)load {
[IOSNetworkState sharedInstance];
}
+ (instancetype)sharedInstance {
static IOSNetworkState *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[IOSNetworkState alloc] init];
});
return instance;
}
- (instancetype)init {
self = [super init];
if (self) {
Network::NetworkMonitor::GetInstance()->DoXXX();
}
return self;
}
然后就悲剧了,APP 起来就crash。crash 的位置是,执行 Network::NetworkMonitor::GetInstance() 方法时,加锁操作crash。原因是Mutex 对象未初始化。
原来,OC 类的 +(void)load 方法,其执行时期是类的加载期。比全局对象(就是我们的Mutex)的初始化要早。当然这个时候,main 函数更加没有得到执行。
自然我们这时候,执行加锁操作就会引发异常了。
5. 总结
简单总结一下,使用c++ 单例一些需要注意的地方:
- 一:使用线程安全的单例方法。
- 二:尽量避免在单例类的构造方法中,使用其他的单例对象。
- 三:不要在类的加载期方法中,使用其他单例对象。其实,在类加载期方法中,不应该涉及业务处理。
C++ 安全单例模式总结的更多相关文章
- C++实现线程安全的单例模式
在某些应用环境下面,一个类只允许有一个实例,这就是著名的单例模式.单例模式分为懒汉模式,跟饿汉模式两种. 首先给出饿汉模式的实现 template <class T> class sing ...
- 23种设计模式--单例模式-Singleton
一.单例模式的介绍 单例模式简单说就是掌握系统的至高点,在程序中只实例化一次,这样就是单例模式,在系统比如说你是该系统的登录的第多少人,还有数据库的连接池等地方会使用,单例模式是最简单,最常用的模式之 ...
- angular2系列教程(十)两种启动方法、两个路由服务、引用类型和单例模式的妙用
今天我们要讲的是ng2的路由系统. 例子
- java设计模式之--单例模式
前言:最近看完<java多线程编程核心技术>一书后,对第六章的单例模式和多线程这章颇有兴趣,我知道我看完书还是记不住多少的,写篇博客记录自己所学的只是还是很有必要的,学习贵在坚持. 单例模 ...
- 设计模式C#合集--单例模式
单例模式 代码: 第一种: private static Singleton singleton = null; private Singleton() { } public static Singl ...
- 设计模式之单例模式(Singleton)
设计模式之单例模式(Singleton) 设计模式是前辈的一些经验总结之后的精髓,学习设计模式可以针对不同的问题给出更加优雅的解答 单例模式可分为俩种:懒汉模式和饿汉模式.俩种模式分别有不同的优势和缺 ...
- GOF23设计模式之单例模式
·核心作用: -保证一个类只有一个实例,并且提供一个访问该实例的全局访问点. ·常见应用场景: -Windows的Task Manager(任务管理器)就是很典型的单例模式 -Windows的Recy ...
- GJM : C#设计模式(1)——单例模式
感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...
- PHP设计模式(四)单例模式(Singleton For PHP)
今天讲单例设计模式,这种设计模式和工厂模式一样,用的非常非常多,同时单例模式比较容易的一种设计模式. 一.什么是单例设计模式 单例模式,也叫单子模式,是一种常用的软件设计模式.在应用这个模式时,单例对 ...
- java设计模式之单例模式(几种写法及比较)
概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...
随机推荐
- Jfinal数据库操作语句中占位符的使用
占位符的优点: 1.增加SQL代码可读性 2.占位符可以预先编译,提高执行效率 3.防止SQL注入 4.用占位符的目的是绑定变量,这样可以减少数据SQL的硬解析,所以执行效率会提高不少 假设要将id从 ...
- Linux实战教学笔记11:linux定时任务
第十一节 linux定时任务 标签(空格分隔): Linux实战教学笔记 ---更多资料点我查看 1.1 定时任务Crond介绍 Crond是linux系统中用来定期执行命令/脚本或指定程序任务的一种 ...
- android webview和 javascript 进行交互
HTML5进行app开发具有开发快,跨平台等优点,但是当客户需要访问照相机或者调用摄像头等硬件的时候,H5就会有限制,必须要调用原生方法进行设备访问.下面简要介绍JS和原生方法互相调用的方法: 1 在 ...
- 前端 tips
1.==和!=操作符会在需要的情况下自动转换数据类型.但===和!==不会,它们会同时比较值和数据类型,这也使得它们要比==和!=快. 2.首次为变量赋值时务必使用var关键字,变量没有声明而直接赋值 ...
- Python变量运算字符串等
一,作用域 操作 name = 'liuyueming' if 1==1:... print name... liuyueming 两次回车执行 修改代码 >>> if 1==1:. ...
- 使用Browserify来实现CommonJS的浏览器加载
前面的话 Nodejs的模块是基于CommonJS规范实现的,可不可以应用在浏览器环境中呢? var math = require('math'); math.add(2, 3); 第二行math.a ...
- 转:Centos6.5_x86安装Redis。
Redis是一个高性能的,开源key-value型数据库.是构建高性能,可扩展的Web应用的完美解决方案,可以内存存储亦可持久化存储.因为要使用跨进程,跨服务级别的数据缓存,在对比多个方案后,决定使用 ...
- [Angularjs]$http.post与$.post
摘要 在angularjs发送post请求的时候,确实很困惑,在传递json数据的时候,总会遇到在服务端无法接受到参数的情况,这里有必要与$.post进行比较学习一下. 一个例子 这里模拟登录的一个场 ...
- NodeJS安装第一个工程
一.刚接触Node.js,下载好安装包后,一路Next,安装好后,结构目录如下 在命令行窗口输入node -v 和npm -v 二.建立一个Node.js工程 1.(控制台窗口)全局安装了expres ...
- 学习笔记TF010:softmax分类
回答多选项问题,使用softmax函数,对数几率回归在多个可能不同值上的推广.函数返回值是C个分量的概率向量,每个分量对应一个输出类别概率.分量为概率,C个分量和始终为1.每个样本必须属于某个输出类别 ...