c++设计模式-单例模式

时间:2024-10-02 15:16:38

单例模式

        单例模式是一种常用的软件设计模式。 在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。

class A;

A a; 这个系统中只能有一个A类对象

控制这个A类型的对象个数,单例模型。系统只有一个对象。

  1. #include <iostream>
  2. using namespace std;
  3. //单例模型
  4. class A {
  5. //为了不让用户可以自己创建对象 必须把构造函数私有化
  6. private:
  7. A() {
  8. a = new A;
  9. }
  10. //这里唯一一个public的接口设置为静态
  11. //用户便不需要创建对象直接类名进行访问
  12. public:
  13. static A* getInstance() {
  14. return a;
  15. }
  16. //成员变量也需要私有化 不然用户可以A::a
  17. //而且需要设置为静态 匹配上面接口的返回值
  18. private:
  19. static A* a;
  20. };
  21. //类外初始化
  22. A* A::a = NULL;
  23. //实现单例的步骤
  24. //1.构造函数私有化
  25. //2.增加静态私有的当前类的指针变量
  26. //3.提供静态的对外接口 可以让用户获得单例对象
  27. int main() {
  28. A::getInstance();
  29. return 0;
  30. }

 单例模型分为懒汉式和饿汉式两种:

  1. #include <iostream>
  2. using namespace std;
  3. //单例模型
  4. //单例 分为懒汉式 和 饿汉式
  5. class Singleton_lazy {
  6. private:
  7. Singleton_lazy() {}
  8. public:
  9. //增加一个条件判断 如果静态变量为空 则创建一个对象返回
  10. static Singleton_lazy* getInstance() {
  11. if (pSingleton == NULL) {
  12. pSingleton = new Singleton_lazy;
  13. }
  14. return pSingleton;
  15. }
  16. //这个释放堆区对象的方式不能提供 太危险了
  17. //只要有一个人释放 大家共享的对象就都没了
  18. /*static void freeSpace() {
  19. if (pSingleton != NULL) {
  20. delete pSingleton;
  21. pSingleton = NULL;
  22. }
  23. }*/
  24. private:
  25. static Singleton_lazy* pSingleton;
  26. };
  27. //类外初始化
  28. Singleton_lazy* Singleton_lazy::pSingleton = NULL;
  29. //饿汉式
  30. class Singleton_hungry {
  31. private:
  32. Singleton_hungry() { cout << "我是饿汉式构造" << endl; }
  33. public:
  34. //可以发现这里没有条件判断了
  35. static Singleton_hungry* getInstance() {
  36. return pSingleton;
  37. }
  38. private:
  39. static Singleton_hungry* pSingleton;
  40. };
  41. //类外初始化 初始化的时候直接创建对象
  42. Singleton_hungry* Singleton_hungry::pSingleton = new Singleton_hungry;
  43. void test01() {
  44. Singleton_lazy* p1 = Singleton_lazy::getInstance();
  45. Singleton_lazy* p2 = Singleton_lazy::getInstance();
  46. if (p1 == p2) {
  47. cout << "两个指针指向同一块内存 是单例" << endl;
  48. }
  49. else {
  50. cout << "不是单例模式" << endl;
  51. }
  52. }
  53. void test02() {
  54. Singleton_hungry* p1 = Singleton_hungry::getInstance();
  55. Singleton_hungry* p2 = Singleton_hungry::getInstance();
  56. if (p1 == p2) {
  57. cout << "两个指针指向同一块内存 是单例" << endl;
  58. }
  59. else {
  60. cout << "不是单例模式" << endl;
  61. }
  62. }
  63. int main() {
  64. cout << "我是main函数" << endl;
  65. test01();
  66. test02();
  67. return 0;
  68. }

 输出结果:

        我是饿汉式构造
        我是main函数
        两个指针指向同一块内存 是单例
        两个指针指向同一块内存 是单例

可以看到 最先输出的并不是main函数的输出 因为当程序走到饿汉式的静态成员类外初始化

的地方时 就直接调用了它的构造函数

两种单例模式下重复的调用接口也都是指向同一块内存

同时需要注意的是在单例模式下 我们是不需要考虑堆区内存释放的问题 首先如果提供一个接口freeSpace()去释放的话 太过于危险 因为大家共享一个对象 一释放全没了 内存泄漏指的是不释放堆区内存 不停的堆积下去会让内存放不下而泄漏 但是我们的单例模型从头到尾只有一个对象 只占一块堆区内存 所以等程序结束后自动释放即可

 单线程基本概念

示例图:

 可以看到 单线程就是顺序往下执行 而多线程就是图片右边有A B两个线程同时执行 但是不一定谁先执行完 当A执行test01 而B执行test02的时候 假设提前给a变量赋值为10 那么就容易产生混乱 如果线程A先一步执行加10操作然后B进入条件判断 那么最后a的结果是20 如果是B在A还没来得及加10之前就判断了 那么a最后结果为10 所以说变量a是进程A B的竞争资源 通常来说解决多线程混乱问题的方式就是给a加一个锁 比如当进程A开始执行test01了那么就会给变量a上锁 知道执行完给a解锁进程B才可以访问变量

单例碰到多线程

懒汉式遇到多线程是不安全的 因为A B线程在执行的时候都会进行判断静态成员变量不为NULL然后new出一个新的对象

饿汉式遇到多线程是安全的 因为饿汉式的静态成员变量在类外初始化 在线程执行之前就已经创建了好了对象 线程A B访问接口的时候返回的都是同一个对象