当前位置: 首页 > news >正文

C++设计模式_创建型模式_单件模式

本文记录单例模式。
单例模式又称为单例模式,是一种创建型模式,适用于产生一个对象的示例。
使用场景:项目中只存在一个对象,比如声音管理系统,一个配置系统,一个文件管理系统,一个日志系统,一个线程池等。

单件类的实现和特点

一个单例模式的实现。

class GameConfig
{
private:GameConfig() {}GameConfig(const GameConfig&) {}GameConfig& operator=(const GameConfig&) {}public:static GameConfig* getInstance(){if (_instance == NULL){_instance = new GameConfig();}return _instance;}private:static GameConfig* _instance;
};GameConfig* GameConfig::_instance = nullptr;void test()
{//GameConfig g1;		// 测试构造函数//GameConfig g2(g1);   // 测试拷贝构造函数	//GameConfig g3 = g1; // 测试拷贝构造函数//// 测试赋值运算符//GameConfig g4;//g4 = g1;// 测试单例GameConfig* g5 = GameConfig::getInstance();GameConfig* g6 = GameConfig::getInstance();if (g5 == g6){cout << "g5 == g6" << endl;}
}

上面分别测试了单例类的构造函数,拷贝构造,拷贝赋值运算符。
单例模式的特点:
1 类的构造函数是私有的;
2 通过Public的静态成员函数getInstance() 创建单例类的对象;
3 将拷贝构造也设置为私有,保证对象可能被复制。

单件类在多线程中可能导致的问题

假如在多线程场景下使用单例模式:当线程1执行完if (instance == nullptr)这句话时,还未new GameConfig()对象,此时由于操作系统调度,切换到了线程2,此时线程2也会进入if (instance == nullptr),这样就可能导致了在多线程情况下,创建了两个单例类对象,这违背了开发者初衷,代码产生混乱。如何解决上面问题呢?
解决方式1:在if中加锁,代码如下所示。加锁方式解决了多个线程同时进入if条件的问题,但是随着而来的执行效率问题:一旦第一次创建成功后,后边即使多个线程也不会再执行_instance == nullptr的条件了,这种加锁的方式相当于对一个只读互斥量加锁,严重影响了程序执行效率。

static GameConfig* getInstance()
{std::lock_guard<std::mutex> lock(_mutex);  // if (_instance == NULL){_instance = new GameConfig();}return _instance;
}

解决方式2:双重加锁。双重加锁代码如下,这种方式时为了提高多线程情况下效率,事实上这种方式确实减少了加锁的频率,提高的效率。这种方式在《C++并发编程实战》书上,作者很不赞同这种加锁方式,记录那个笔记时,再来看这种方式的缺点。

static GameConfig* getInstance()
{if (_instance == nullptr){std::lock_guard<std::mutex> lock(_mutex);  // if (_instance == NULL){_instance = new GameConfig();}}return _instance;
}

双重加锁代码的问题:内存访问重新排序,导致双重锁定失效的问题。上边的代码instance = new GameConfig(),这行代码大概分三个步骤完成:首先调用malloc分配内存,然后调用构造函数初始化这块内存,最后让instance 指向这块内存。但是,由于编译器优化等原因,上边的步骤被重新排序,执行顺序可能时132,这样麻烦就来了,因为instance指向这块new出来的内存,_instance == NULL就不成立了,表示已经new 成功了,可是这个new出来的内存并没有被初始化,当一个线程使用这个内存时,就会报错,这就是内存访问重新排序的问题。
实际使用时,先在main()中初始化,这样不必加锁也可实现线程的安全的单例模式。
书中提供的一个示例:

class GameConfig
{
private:GameConfig() {};GameConfig(const GameConfig& tmpobj);GameConfig& operator = (const GameConfig& tmpobj);~GameConfig() {};
public:static GameConfig* getInstance(){GameConfig* tmp = m_instance.load(std::memory_order_relaxed);std::atomic_thread_fence(std::memory_order_acquire);if (tmp == nullptr){std::lock_guard<std::mutex> lock(m_mutex);tmp = m_instance.load(std::memory_order_relaxed);if (tmp == nullptr){tmp = new GameConfig();std::atomic_thread_fence(std::memory_order_release);m_instance.store(tmp, std::memory_order_relaxed);}}return tmp;}
private:static atomic<GameConfig*> m_instance;static std::mutex m_mutex;
};
std::atomic<GameConfig*> GameConfig::m_instance;
std::mutex GameConfig::m_mutex;

饿汉式与懒汉式

懒汉式单例模式在调用getInstance()时构造对象;
饿汉式单例模式在编译时候就构造对象;

饿汉式 单例模式

// 饿汉式 单例模式
class GameConfig
{
private:GameConfig() {};GameConfig(const GameConfig& tmpobj);GameConfig& operator = (const GameConfig& tmpobj);~GameConfig() {};
public:static GameConfig* getInstance(){return m_instance;}private:static GameConfig* m_instance;
private:class GC{public:~GC(){if (m_instance != nullptr){delete m_instance;m_instance = nullptr;}}};static GC gc;
};
GameConfig* GameConfig::m_instance = new GameConfig();
GameConfig::GC GameConfig::gc;

懒汉式

// 懒汉式单例模式另一种写法
class GameConfig
{
private:GameConfig() {};GameConfig(const GameConfig& tmpobj);GameConfig& operator = (const GameConfig& tmpobj);~GameConfig() {};public:static GameConfig* getInstance(){return &m_instance;}
private:static GameConfig m_instance;
};
GameConfig GameConfig::m_instance;

单件类对象内存释放问题

上边的饿汉式单例模式中,在类中加了一个GC类,在GC类的析构函数中释放单例模式的对象。

单件类定义、UML图及另外一种实现方法

单件设计模式定义:保证一个类仅有一个实例存在,同时提供能对该实例访问的全局方法(getInstance成员函数)。
下面也是一种常见的单例模式写法。

class GameConfig
{
private:GameConfig() {};GameConfig(const GameConfig& tmpobj);GameConfig& operator = (const GameConfig& tmpobj);~GameConfig() {};public:static GameConfig& getInstance(){static GameConfig instance; // 局部静态变量,在运行时期初始化;return instance;}
};
void test()
{static int a = 100;  // 编译时期初始化GameConfig& g1 = GameConfig::getInstance();
}

单例模式UML

类和类之间是聚合关系。
在这里插入图片描述

http://www.dtcms.com/a/407499.html

相关文章:

  • 进阶:基于 dlib 的 68 点人脸关键点检测实现
  • 设计师培训心得北京seo诊断
  • (超详细,于25年更新版) VMware 虚拟机安装以及Linux系统—CentOS 7 部署教程
  • 微信兼职平台网站开发多个域名多国语言网站seo优化
  • JavaScript逆向补环境逆向WASM的思路
  • Android 13.0 Framework 实现应用通知使用权默认开启的技术指南
  • 厦门网站的关键词自动排名大都会app可以删记录吗
  • 网站优化标题百姓网免费发布信息网下载
  • DaemonSet使用示例
  • 台州建网站北京装饰公司电话
  • docker容器的三大核心技术UnionFS(下)
  • 4.6 BRDF
  • Python GIL全局解释器锁技术演进
  • 小学老师在哪个网站做ppt做羊毛毡的网站
  • 模块化神经网络
  • Python多线程:让程序 “多线作战” 的秘密武器
  • 黟县方坑岭影视基地三剧连拍开机 《生死制暴》影视赋能乡村振兴
  • 微信网站打不开海南搜索引擎优化
  • 国产化(银河麒麟_海光CPU)消息中间件选型及安装
  • 宁波品牌网站推广优化公司章丘营销型网站设计公司
  • p6spy 打印完整sql
  • 【ARM】MDK-Functions界面设置
  • 沈阳市建设局网站首页网站的运行与维护
  • 昌宁县住房和城乡建设网站订阅号做流量 那些电影如何链接网站
  • 【LVS入门宝典】LVS调度算法轮询(RR)深度解析:从原理到实战的公平调度之道
  • udhcpc, udhcpd由 BusyBox编译出来就好
  • 前端 CORS 深度解析
  • HT81696 概述
  • PMP-项目管理-PMBOK第六版_中文版:引论
  • 上海网站建站建设自己做的网站在百度怎么发布