首页 > 编程语言 >C++ 设计模式之单例模式

C++ 设计模式之单例模式

时间:2023-07-07 13:46:32浏览次数:33  
标签:include Singelton getInstance C++ SingeltonThread 实例 线程 单例 设计模式

设计模式之单例模式
  保证一个类仅有一个实例,并提供一个访问他的全局访问点。通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且可以提供一个访问该实例的接口。其UML图如下:

 其示例代码如下:

 1 // SingeltonModel.h文件
 2 #pragma once
 3 // 线程不安全类
 4 class Singelton
 5 {
 6 private:
 7     Singelton() {}
 8     static Singelton * m_singel;
 9 public:
10     static Singelton * getInstance()
11     {
12         if (nullptr == m_singel)
13         {
14             std::cout << "创建实例" << std::endl;
15             m_singel = new Singelton();
16         }    
17         return m_singel;
18     }
19 };
20 Singelton * Singelton::m_singel = nullptr;

测试代码如下:

 1 #include <iostream>
 2 #include "SingeltonModel.h"
 3 
 4 int main()
 5 {
 6     using namespace std;
 7     // 单例模式
 8     Singelton * p1 = Singelton::getInstance();
 9     Singelton * p2 = Singelton::getInstance();
10     if (p1 == p2)
11         std::cout << "一致" << std::endl;
12     else
13         std::cout << "不一致" << std::endl;
14 
15     getchar();
16     return 0;
17 }

测试结果如下图:

 以上代码是有安全隐患的,如果该类在多线程中使用的话。当多个线程都第一次访问该类的是实例的时候有可能会出现创建两个实例,这样就出现内存泄漏了。并且两次的到的实例不是同一个,如下代码:

 1 #include <iostream>
 2 #include <thread>
 3 #include <Windows.h>
 4 #include "SingeltonModel.h"
 5 
 6 void func1()
 7 {
 8     Sleep(1);
 9     Singelton * p1 = Singelton::getInstance();
10     std::cout << "func1 thread:" << p1 << std::endl;
11 }
12 
13 void func2()
14 {
15     Sleep(1);
16     Singelton * p2 = Singelton::getInstance();
17     std::cout << "func2 thread:" << p2 << std::endl;
18 }
19 
20 int main()
21 {
22     using namespace std;
23     // 单例模式
24     std::thread thread1(func1), thread2(func2);
25     thread1.detach();
26     thread2.detach();
27     std::cout << "主线程" << std::endl;
28 
29     getchar();
30     return 0;
31 }

输出结果如下图:

  由上图可看出,创建了两次实例,两个线程得到的实例并不是同一个。这是因为两个线程同时运行,当调用getInstance()时,实例并没有创建,于是两个线程都进入了创建实例的代码块,于是就创建了两个实例。而第一个被创建的实例被顶替后也没有被释放,这就是内存泄漏,还有先调用到创建实例的线程还得到了错误的实例,这样会造成逻辑错误的。

  解决办法有两种,一种办法是在所有要访问该类实例的代码执行前该类先创建实例,另一种是在该类的访问实例的代码中加入线程同步的内容。实例代码如下:

方法一:

 1 #include <iostream>
 2 #include <thread>
 3 #include <Windows.h>
 4 #include "SingeltonModel.h"
 5 
 6 void func1()
 7 {
 8     Sleep(1);
 9     Singelton * p1 = Singelton::getInstance();
10     std::cout << "func1 thread:" << p1 << std::endl;
11 }
12 
13 void func2()
14 {
15     Sleep(1);
16     Singelton * p2 = Singelton::getInstance();
17     std::cout << "func2 thread:" << p2 << std::endl;
18 }
19 
20 int main()
21 {
22     using namespace std;
23     // 单例模式
24     // 在开启线程前,先获取一次实例。
25     Singelton::getInstance();
26     std::thread thread1(func1), thread2(func2);
27     thread1.detach();
28     thread2.detach();
29     std::cout << "主线程" << std::endl;
30 
31     getchar();
32     return 0;
33 }

输出结果如下图:

 方法二:

 1 // SingeltonModel.h文件
 2 #include <mutex>
 3 #pragma once
 4 
 5 // 线程安全类
 6 class SingeltonThread
 7 {
 8 private:
 9     SingeltonThread() {}
10     static SingeltonThread * m_instance;
11     static std::mutex m_mutex;
12 public:
13     static SingeltonThread * getInstance()
14     {
15         if (nullptr == m_instance)
16         {
17             m_mutex.lock();
18             if (nullptr == m_instance)
19                 m_instance = new SingeltonThread();
20             m_mutex.unlock();
21         }
22         return m_instance;
23     }
24 };
25 SingeltonThread * SingeltonThread::m_instance = nullptr;
26 std::mutex SingeltonThread::m_mutex;

测试代码如下:

 1 #include <iostream>
 2 #include <thread>
 3 #include <Windows.h>
 4 #include "SingeltonModel.h"
 5 
 6 void func1()
 7 {
 8     Sleep(1);
 9     SingeltonThread * p1 = SingeltonThread::getInstance();
10     std::cout << "func1 thread:" << p1 << std::endl;
11 }
12 
13 void func2()
14 {
15     Sleep(1);
16     SingeltonThread * p2 = SingeltonThread::getInstance();
17     std::cout << "func2 thread:" << p2 << std::endl;
18 }
19 
20 int main()
21 {
22     using namespace std;
23     // 单例模式
24     // 线程安全
25     std::thread thread1(func1), thread2(func2);
26     thread1.detach();
27     thread2.detach();
28     std::cout << "主线程" << std::endl;
29     
30     getchar()
31     return 0;
32 }

输出结果如下图:

  这两种方法都各有优势,方法一实现简单,但在复杂的系统中不太安全。方法二实现稍微复杂,而且每次访问实例都要lock(mutex),消耗更大。但是方法二在任何情况下都很安全。总之方法一适合在系统不是太复杂的情况下使用,方法二在系统比较复杂的情况下使用。

标签:include,Singelton,getInstance,C++,SingeltonThread,实例,线程,单例,设计模式
From: https://www.cnblogs.com/ybqjymy/p/17534732.html

相关文章

  • C++学生健康信息收集系统[2023-07-06]
    C++学生健康信息收集系统[2023-07-06]学生健康信息收集系统简介一、 问题描述为了应对新型冠状病毒疫情,学校需要开发一个能够每天收集全校学生健康信息的系统,便于学校管理。不同学院以及学校的管理员,需要能方便地查看和导出健康状况异常的学生列表,并能对各类信息进行查看和统计......
  • C/C++学生通讯录管理系统[2023-07-06]
    C/C++学生通讯录管理系统[2023-07-06]一、设计要求1、题目利用C++语言实现一个学生通讯录管理系统,系统中需要实现的功能如下:(1)添加学生信息:向通讯录中添加新人,信息包括(学生姓名、性别、年龄、联系电话、家庭住址等),最多记录100人。(2)显示学生信息:显示通讯录中所有学生信息。(3)删......
  • C++ 设计模式之桥接模式
    设计模式之桥接模式桥接模式,将抽象部分与它的实现部分分离,使它们都可以独立地变化。这里说的实现分离,并不是说然抽象类与派生类分离,这没有任何意义。实现指的是抽象类和它的派生类用来实现自己的对象。其UML图如下: 结合上图理解一下其定义。抽象部分指的是Abstraction......
  • C/C++订餐管理系统[2023-07-07]
    C/C++订餐管理系统[2023-07-07]1、订餐管理系统要求实现饭店的订餐信息管理,包括菜单管理、订单管理、统计分析。实现菜单信息(菜号、菜名、价格、成本)的增删改查;实现订单管理(订单号、就餐人数、下单时间、订单总价、订单包含的所有菜品(菜号、数量))。系统功能包括以下方面:A、菜......
  • 学习C++这一篇就够了(进阶篇)
    ​内存模型C++在执行程序的时候,将内存方向划分为4个区域:代码区:存放二进制代码,由操作系统进行管理全局区:存放全局变量、静态变量、常量,程序结束后由操作系统释放栈区:存放函数参数、局部变量,由编译器自动分配和释放堆区:由开发者申请分配和释放,若程序员不释放,程序结束由......
  • C++ 中的函数重载
     在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。您不能仅通过返回类型的不同来重载函数。https://www.121mu.com/bkjq211/......
  • C++ 多态
     多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。C++多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。下面的实例中,基类Shape被派生为两个类https://www.121mu.com/czss/......
  • C++ 中的运算符重载
     您可以重定义或重载大部分C++内置的运算符。这样,您就能使用自定义类型的运算符。重载的运算符是带有特殊名称的函数,函数名是由关键字operator和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。https://www.micsoon.com/bkzn19/......
  • C++ std::chrono 计时器的使用
    使用标准库 #include<chrono>intmain(){ autostampBeg=std::chrono::steady_clock::now(); //dosometing Sleep(2400); autostampEnd=std::chrono::steady_clock::now(); doubletime_second=std::chrono::duration<double>(stampEnd-sta......
  • C++银行存取款管理系统[2023-07-06]
    C++银行存取款管理系统[2023-07-06]银行存取款管理系统一、 功能描述能够输入和查询客户存款取款记录。在客户文件中,每个客户是一条记录,包括编号、客户姓名、支取密码、客户地址、客户、账户总金额;在存取款文件中,每次存取款是一条记录,包括编号、日期、类别、存取数目、经办人。......