C++设计模式——单例类
C++设计模式——单例类
本文假设有一个Manager管理类,探讨单例类懒汉/饿汉模式的实现,和单例类的多线程安全性,最后介绍Meyers Singleton写法。
懒汉模式
当第一次要用单例类的时候,再产生实例。是一种典型的拖延(lazy)策略。
类声明:
class Manager
{
public:
~Manager();
static Manager* getInstance();//提供单例对象访问
static void deleteInstance();//删除单例对象
void dosomething();
protected:
Manager();//构造函数声明为 protected
static Manager* s_Manager;//单例对象指针
};
类定义:
//单例对象指针初始化为nullptr,防止指向了未定义的数据
Manager* Manager::s_Manager = nullptr;
//提供单例类对象访问
Manager* Manager::getInstance(){
//当没有存在实例时(一般是指准备第一次用)时,才生成新实例
if(!s_Manager)
s_Manager = new CacheManger();
return s_Manager;
}
//删除单例类
void Manager::deleteInstance(){
if(s_Manager){
deleted s_Manager;
s_Manager = nullptr;//别忘了赋予空指针,否则指向未定义数据
}
}
void Manager::dosomething(){
//dosometing
}
使用此类时就可以通过:
Manager::getInstance()->dosomething();
来运用单例类来做某些操作了。
懒汉模式with线程安全
上面的例子,并不能保证线程安全。
假如没有实例时,某两个线程都几乎同时使用getInstance(),那么很可能会产生2份实例,其中一份还会变成泄露的内存。
为了解决线程安全问题,本文使用了C++11<mutex>的std::mutex作为互斥锁,在类额外增加了一个静态变量std::mutext s_mtx;。
//提供单例类对象访问
Manager* Manager::getInstance() {
if (!s_Manager) //检查
{//上锁
std::lock_guard<std::mutex> lock(s_mtx);
if (!s_Manager)
s_Manager = new Manager();
}//解锁
return s_Manager;
}
//删除单例类
void Manager::deleteInstance() {
if (s_Manager) //检查
{//上锁
std::lock_guard<std::mutex> lock(s_mtx);
if (s_Manager)
{
delete s_Manager;
s_Manager = nullptr;
}
}//解锁
}
为什么不是(上锁,检查,操作,解锁)或者(检查,上锁,操作,解锁),而是使用了双重检查(检查,上锁,检查,操作,解锁)?
- 上锁的成本远远比检查空指针要高,且当需要产生实例时才需要锁操作。而实际上大量多次使用getInstance时(因为已经产生了实例)并不需要上锁,若先上锁,则会严重造成性能阻塞。
- 仅仅是检查后再上锁,则根本没有做到任何线程安全。
饿汉模式
饿汉模式与懒汉模式相反,一开始就生成唯一实例。这样就不用检查是否存在实例,而且也无需考虑产生实例时的线程安全。
class Manager {
public:
~Manager();
//提供单例对象访问
static Manager* getInstance();
void dosomething();
protected:
//构造函数声明为 保护方法
Manager();
//单例对象指针
static Manager* s_Manager;
};
//提供单例类对象访问
Manager* Manager::getInstance(){
return s_Manager;
}
使用方法:
Manager::getInstance()->dosomething();
可以看到代码比懒汉模式简单多了。在大量使用检查空指针造成的性能瓶颈而内存始终充足时,可以考虑使用饿汉模式
Meyers Singleton
目前最推荐的C++单例写法
class Manager {
public:
static Manager& Instance() {
static Manager theManager;
return theManager;
}
private:
Manager();
Manager(Manager const&);
Manager& operator = (Manager const&);
~Manager();
};
这段代码很简单,虽然看上去和懒汉模式类似,只是static变量的位置从类移动到了实例获取函数内。但是实际上由于C++的机制,当第一次调用该函数时,实例才会被构建出来。这样既可以得到饿汉模式的线程安全,又可以有懒汉模式的按需分配的功能。
应用场景
单例类设计模式算是比较经典的一个模式,但是需要注意,它并不是想象中那么美好。
- 它是一种换皮的全局变量。
- 它促进了耦合。
- 它可能对并发不友好(取决于你使用的单例写法)。
一些替代方案:
- 当你仅需要全局可见的方法时,应该用类静态方法而不是一个类实例。
- 尽可能为实例提供其它便捷的访问方式(传参/基类获取/服务定位器获取等),而不是通过提供全局可见的访问方式。
- 如果你只是需要类保证只有唯一对象而不需要全局性,那么应对外封闭获取实例接口(其实就是不可全局获取实例的)。
所以要注意单例类设计模式不应被泛用,通过上面的替代方案多多少少也就减少了很多不必要的单例设计。
C++设计模式——单例类的更多相关文章
- 设计模式——懒汉式单例类PK饿汉式单例类
前言 我们都知道生活中好多小软件,有的支持多IP在线,有的仅仅局限于单个IP在线.为什么这样设计,在软件开发阶段就是,有需求就是发展.这就是软件开发的一个设计模式--懒汉式单例类和饿汉式单例类. 内容 ...
- 游戏设计模式——C++单例类
前言: 本文将探讨单例类设计模式,单例类的懒汉模式/饿汉模式,单例类的多线程安全性,最后将利用C++模板减少单例类代码量. 本文假设有一个Manager管理类,并以此为探究单例类的设计模式. 懒汉模式 ...
- 设计模式(java) 单例模式 单例类
·单例类 单实例类,就是这个类只能创建一个对象,保证了对象实例的唯一性. 1.单例模式( Singleton Pattern) 是一个比较简单的模式, 其定义如下:Ensure a class has ...
- iOS中编写单例类的心得
单例 1.认识过的单例类有哪些: NSUserDefaults.NSNotificationCenter.NSFileManager.UIApplication 2.单例类 单例类某个类在代码编写时使 ...
- (七)boost库之单例类
(七)boost库之单例类 一.boost.serialzation的单件实现 单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.通过单例模式可以保证系统中一个类只有一 ...
- GCD实现简单的单例类-Singletion
什么是单例模式 1.单例模式是一个类在系统中只有一个实例对象.通过全局的一个入口点对这个实例对象进行访问.在 iOS 开发中,单例模式是非常有用的一种设计模式.如 下图,是一个简单单例模式的 UML ...
- java设计模式--单例
GOF23(group of four)---由4个大牛总结的JAVA23种常用的设计模式,归根结底都是为了代码的可扩展性. 设计模式中一种比较重要的思想就是:开闭原则,尽量做到对扩展开放,对修改关闭 ...
- C++解析(27):数组、智能指针与单例类模板
0.目录 1.数组类模板 1.1 类模板高效率求和 1.2 数组类模板 1.3 堆数组类模板 2.智能指针类模板 2.1 使用智能指针 2.2 智能指针类模板 3.单例类模板 3.1 实现单例模式 3 ...
- ios开发之 -- 单例类
单例模式是一种软件设计模式,再它的核心结构中指包含一个被称为单例类的特殊类. 通过单例模式可以保证系统中一个类只有一个势力而且该势力易于外界访问,从而方便对势力个数的控制并节约系统资源.如果希望在系统 ...
- Singleton单例类模式
body, table{font-family: 微软雅黑; font-size: 10pt} table{border-collapse: collapse; border: solid gray; ...
随机推荐
- 【高手训练】【RMQ】奶牛排队
第一题由于过水,就没写awa 大概就是这样了.题意就是求一个最长的区间使得区间的左边是它的最小值,区间右边是他的最大值第一个想法肯定是暴力枚举啦awa但是这个是O(n^3)的,绝对的不可过awa 思考 ...
- 基本操作:vscode快捷键
1.复制,剪切 补选中具体内容的话,光标放在这一行的任何位置,输入Ctrl+C,就表示已经复制这一行了:直接Ctrl+V可以粘贴: 截切也一样:光标放在这一行的任何位置,输入Ctrl+X,就表示已 ...
- python 远程操作svn
SVN操作脚本 安装模块 pip install pywinrm 脚本如下 #!/usr/bin/env python3 # coding=utf-8 # author:LJX # describe: ...
- VRRP相关简述
VRRP 诞生原因 单网关,出问题时,旗下所有主机无法通信. 多网关,容易产生网关冲突. 而,VRRP能够在不改变组网的情况下,将多台路由器虚拟成一个虚拟路由器,通过配置虚拟路由器的IP地址为默认网关 ...
- MacOS X终端里SSH会话管理
http://codelife.me/blog/2012/09/01/ssh-session-profile-management-in-terminal-of-macos-x/ 本文介绍如何在终端里 ...
- Java Junit单元测试(入门必看篇)
Hi i,m JinXiang 前言 本篇文章主要介绍单元测试工具Junit使用以及部分理论知识 欢迎点赞 收藏 留言评论 私信必回哟 博主收将持续更新学习记录获,友友们有任何问题可以在评论区留言 ...
- CodeChef Starters 9 Division 3 (Rated) India Fights Corona
原题链接 India Fights Corona 题意: 有\(n\)个城市,\(m\)条道路,其中有些城市自己有医院,所以可以在自己城市做核酸检测,那么花费就只有就医费用,而对于那些自己没有医院的城 ...
- Ubuntu 20.04 挂载局域网络共享硬盘
创建挂载目录 mkdir /media/nas 创建认证文件.若无密码可以忽略这一步. sudo vim /root/.examplecredentials 按照以下格式写入用户名密码: userna ...
- 在Vue3中使用Element-Plus分页(Pagination )组件
在Vue3中使用Element-Plus分页(Pagination )组件 开发过程中数据展示会经常使用到,同时分页功能也会添加到页面中. 记:在Vue3中使用Element-Plus分页组件与表格数 ...
- Ubuntu 18.04替换默认软件源
安装Ubuntu 18.04后,默认源在国外,可以替换为国内的源以提升访问速度 参考https://mirrors.ustc.edu.cn/repogen/ sudo vi /etc/apt/sour ...