游戏设计模式——C++单例类
前言:
本文将探讨单例类设计模式,单例类的懒汉模式/饿汉模式,单例类的多线程安全性,最后将利用C++模板减少单例类代码量。
本文假设有一个Manager管理类,并以此为探究单例类的设计模式。
懒汉模式
懒汉模式:顾名思义,是一种典型的拖延(lazy)策略。当第一次要用单例类的时候,再产生实例。
类声明:
class Manager{
public:
~Manager();
//提供单例对象访问
static Manager* getInstance();
//删除单例对象
static void deleteInstance();
void dosomething();
protected:
//构造函数声明为 保护方法
Manager();
//单例对象指针
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 Singleton {
public:
static Singleton& Instance() {
static Singleton theSingleton;
return theSingleton;
}
private:
Singleton();
Singleton(Singleton const&);
Singleton& operator=(Singleton const&);
~Singleton();
};
这段代码很简单,虽然看上去和懒汉模式类似,只是static变量的位置从类移动到了实例获取函数内。
但是实际上由于C++的机制,当第一次调用该函数时,实例才会被构建出来。
这样既可以得到饿汉模式的线程安全,又可以有懒汉模式的按需分配的功能。
应用场景注意
单例类设计模式算是比较经典的一个模式,但是需要注意,它并不是想象中那么美好。
- 它是一种换皮的全局变量。
- 它促进了耦合。
- 它可能对并发不友好(取决于你使用的单例写法)。
一些替代方案:
- 当你仅需要全局可见的方法时,应该用类静态方法而不是一个类实例。
- 尽可能为实例提供其它便捷的访问方式(传参/基类获取/服务定位器获取等),而不是通过提供全局可见的访问方式。
- 如果你只是需要类保证只有唯一对象而不需要全局性,那么应对外封闭获取实例接口(其实就是不可全局获取实例的)。
所以要注意单例类设计模式不应被泛用,通过上面的替代方案多多少少也就减少了很多不必要的单例设计。
游戏设计模式系列-其他文章:
https://www.cnblogs.com/KillerAery/category/1307176.html
游戏设计模式——C++单例类的更多相关文章
- 设计模式——懒汉式单例类PK饿汉式单例类
前言 我们都知道生活中好多小软件,有的支持多IP在线,有的仅仅局限于单个IP在线.为什么这样设计,在软件开发阶段就是,有需求就是发展.这就是软件开发的一个设计模式--懒汉式单例类和饿汉式单例类. 内容 ...
- 设计模式(java) 单例模式 单例类
·单例类 单实例类,就是这个类只能创建一个对象,保证了对象实例的唯一性. 1.单例模式( Singleton Pattern) 是一个比较简单的模式, 其定义如下:Ensure a class has ...
- iOS中编写单例类的心得
单例 1.认识过的单例类有哪些: NSUserDefaults.NSNotificationCenter.NSFileManager.UIApplication 2.单例类 单例类某个类在代码编写时使 ...
- iOS 设计模式之单例
设计模式:单例 一. 单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并 ...
- OC中的单例设计模式及单例的宏抽取
// 在一个对象需要重复使用,并且很频繁时,可以对对象使用单例设计模式 // 单例的设计其实就是多alloc内部的allocWithZone下手,重写该方法 #pragma Person.h文件 #i ...
- (七)boost库之单例类
(七)boost库之单例类 一.boost.serialzation的单件实现 单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.通过单例模式可以保证系统中一个类只有一 ...
- GCD实现简单的单例类-Singletion
什么是单例模式 1.单例模式是一个类在系统中只有一个实例对象.通过全局的一个入口点对这个实例对象进行访问.在 iOS 开发中,单例模式是非常有用的一种设计模式.如 下图,是一个简单单例模式的 UML ...
- C++解析(27):数组、智能指针与单例类模板
0.目录 1.数组类模板 1.1 类模板高效率求和 1.2 数组类模板 1.3 堆数组类模板 2.智能指针类模板 2.1 使用智能指针 2.2 智能指针类模板 3.单例类模板 3.1 实现单例模式 3 ...
- ios开发之 -- 单例类
单例模式是一种软件设计模式,再它的核心结构中指包含一个被称为单例类的特殊类. 通过单例模式可以保证系统中一个类只有一个势力而且该势力易于外界访问,从而方便对势力个数的控制并节约系统资源.如果希望在系统 ...
随机推荐
- Thymeleaf常用语法:模板文件中表达式调用Java类的静态方法
在模板文件的表达式中,可以使用“${T(全限定类名).方法名(参数)}”这种格式来调用Java类的静态方法. 开发环境:IntelliJ IDEA 2019.2.2Spring Boot版本:2.1. ...
- JupyterLab安装与配置虚拟环境
JupyterLab安装: 推荐使用miniconda,相比于anaconda更加简洁,下载连接:https://mirrors.tuna.tsinghua.edu.cn/anaconda/minic ...
- Saltstack_使用指南18_API
1. 主机规划 salt 版本 [root@salt100 ~]# salt --version salt (Oxygen) [root@salt100 ~]# salt-minion --versi ...
- Codeforces Round #598 (Div. 3)
传送门 A. Payment Without Change 签到. Code /* * Author: heyuhhh * Created Time: 2019/11/4 21:19:19 */ #i ...
- bzoj 5218: [Lydsy2017省队十连测]友好城市
题意: 这题显然直接tarjan是做不了的. 这里安利另一个求SCC的算法Kosaraju,学习的话可以见这篇博客 于是结合莫队,我们有了个暴力. 发现主要瓶颈是dfs过程中找最小的未经过的点,我们用 ...
- SQL Server 移位运算符
参考链接:http://dataeducation.com/bitmask-handling-part-4-left-shift-and-right-shift/ SQL Server 官网的连接:h ...
- [HDU6288]Tree
题目 题解 首先读题就很成问题....英语咋办呐!!! 直接考虑有点复杂,直接分析每一条边能否被选入最终答案.对于这条边,看看他的\(size[v]\) 与 \(n-size[v]\) 是否都大于等于 ...
- Javascript是如何工作的?
作为一个前端开发者或者全栈开发者,一定非常熟练Javascript.程序员社区Stack Overflow的调查结果显示,Javascript是最常用的编程语言,连续多年排在第一名.发明Javascr ...
- Note | 用Hugo搭建博客并部署到GitHub Pages
目录 1. 本地搭建 1.1 安装Hugo 1.2 创建站点 1.3 新建页面和文章 1.4 使用主题 1.5 修改配置文件 1.6 预览 2. 部署 之前担心过现有博客平台(如博客园,CSDN)突然 ...
- C#中char[]与string之间的转换;byte[]与string之间的转化
目录 1.char[]与string之间的转换 2.byte[]与string之间的转化 1.char[]与string之间的转换 //string 转换成 Char[] string str=&qu ...