• 单例模式(C++编码)


    一、什么是单例模式

    单例模式(Singleton Pattern)是使用最广泛的设计模式之一,其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。单例模式常用于资源管理,例如日志、线程池。

    定义一个单例类大致分为如下三步:

    1、私有化它的构造函数,以防止外界创建单例类的对象;(单例类只能提供私有的构造函数,即保证不能随意创建该类的实例)

    2、使用类的私有静态指针变量指向类的唯一实例;(因为构造函数是私有的,其他对象不能创建单例类的实例,只能是单例类自己来创建)

    3、使用一个公有的静态方法获取该实例。(由于该类的构造函数是私有的,外界无法通过new去获取它的实例,那么就必须提供一个静态的公有方法,该方法创建或者获取它本身的静态私有对象并返回)

    二、单例模式实现方案(C++编码)

    1、单例的两种实现方式

    懒汉式:故名思义,懒汉很懒只有饿了才会去找吃的。也就是说,只有在需要使用的时候才会去实例化。

    饿汉式:饿了肯定要饥不择食。在单例类定义的时候就进行实例化。

    2、懒汉式单例模式

    简单的懒汉式单例模式编码如下:

     1 // Singleton.h
     2 #pragma once
     3  4 class Singleton
     5 {
     6 public:
     7     static Singleton *GetInstance();
     8  9 private:
    10     Singleton();
    11     Singleton(const Singleton &);
    12     Singleton& operator=(const Singleton &);
    13 14     static Singleton *s_instance;
    15 };
    16 17 // Singleton.cpp
    18 #include <iostream>
    19 #include "Singleton.h"
    20 21 Singleton *Singleton::s_instance = NULL;
    22 23 Singleton::Singleton() {}
    24 Singleton::Singleton(const Singleton &) {}
    25 Singleton& Singleton::operator=(const Singleton &) {}
    26 27 Singleton* Singleton::GetInstance()
    28 {
    29     if(nullptr == s_instance)
    30     {
    31         s_instance = new Singleton();
    32     }
    33     return s_instance;
    34 }

    (1)默认构造函数是私有的,外部不能进行单例类的实例化;

    (2)拷贝构造函数和赋值运算符也是私有的,以禁止拷贝和赋值;

    (3)具有一个私有的静态成员指针 s_instance,指向唯一的实例;

    (4)提供一个公有的静态成员函数用于返回实例,如果实例为 NULL,则进行实例化。

    3、饿汉式单例模式

    简单的饿汉式单例模式编码如下:

     1 // Singleton.h
     2 #pragma once
     3  4 class Singleton
     5 {
     6 public:
     7     static Singleton *GetInstance();
     8  9 private:
    10     Singleton();
    11     Singleton(const Singleton &);
    12     Singleton &operator=(const Singleton &);
    13 14     static Singleton *s_instance;
    15 };
    16 17 // Singleton.cpp
    18 #include <iostream>
    19 #include "Singleton.h"
    20 21 Singleton *Singleton::s_instance = new Singleton();
    22 23 Singleton::Singleton() {}
    24 Singleton::Singleton(const Singleton &) {}
    25 Singleton& Singleton::operator=(const Singleton &) {}
    26 27 Singleton* Singleton::GetInstance()
    28 {
    29     return s_instance;
    30 }

    说明:与懒汉式单例模式不同之处是,饿汉式在全局作用域进行单例类的实例化,并用此实例初始化单例类的静态成员指针 s_instance。

    4、线程安全问题

    懒汉式:如果有两个线程同时获取单例类的实例,都发现实例不存在,则都会进行实例化,就会产生两个实例都要赋值给 s_instance,这是是非常不安全的,因而考虑为实例化过程加锁。

     1 Singleton* Singleton::GetInstance()
     2 {
     3     lock();
     4     if(nullptr == s_instance)
     5     {
     6         s_instance = new Singleton();
     7     }
     8     unlock();
     9     
    10     return s_instance;
    11 }

    但这个获取实例的方法存在性能问题,每次获取实例的时候都要先上锁,然后再解锁,如果有很多线程的话会造成大量线程的阻塞,改进成双检锁后编码如下:

     1 Singleton* Singleton::GetInstance()
     2 {
     3     if(nullptr == s_instance)
     4     {
     5          lock();
     6          if(nullptr == s_instance)
     7          {
     8             s_instance = new Singleton();
     9          }
    10          unlock();
    11     }
    12     return s_instance;
    13 }

    绝大多数情况下,获取实例时都是直接返回实例,这时候不会涉及到上锁、解锁的问题。只有在没有实例的时候,才会涉及上锁、解锁,这种情况是很少的。这个获取实例的方法对性能几乎无影响。

    局部静态初始化方法(或称之为 Meyers' Singleton,C++11标准):

     1 class Singleton
     2 {
     3 public:
     4     static Singleton& GetInstance()
     5     {
     6         static Singleton instance;
     7         return instance;
     8     }
     9     
    10 private:
    11     Singleton() {}
    12     Singleton(const Singleton &) {}
    13     Singleton& operator=(const Singleton &) {}
    14 };

    饿汉式:程序运行初期就进行了单例类实例化,不存在上述的线程安全问题。

    5、对象释放问题

    一般情况下,单例类的实例都是常驻内存的,一直存在于进程的生命周期,因此不需要手动释放。如果的确需要释放实例占用的内存,一定不能在单例类的析构函数中进行delete操作,这样会造成无限循环,可以考虑增加一个 destroy 方法用于释放内存(或使用智能指针),或者在单例类中定义一个内嵌的垃圾回收类,下面给出垃圾回收类的简单实现。

     1 // Singleton.h
     2 #pragma once
     3  4 class Singleton
     5 {
     6 public:
     7     static Singleton *GetInstance();
     8  9 private:
    10     class CGarbo
    11     {
    12     public:
    13         ~CGarbo()
    14         {
    15             if(nullptr != Singleton::s_instance)
    16             {
    17                 delete Singleton::s_instance;
    18             }
    19         }
    20     };
    21 22     static CGarbo s_gb;
    23 24 private:
    25     Singleton();
    26     Singleton(const Singleton &);
    27     Singleton& operator=(const Singleton &);
    28 29 private:
    30     static Singleton *s_instance;
    31 };
    32 33 // Singleton.cpp
    34 #include <iostream>
    35 #include "Singleton.h"
    36 37 Singleton *Singleton::s_instance = NULL;
    38 39 Singleton::Singleton() {}
    40 Singleton::Singleton(const Singleton &) {}
    41 Singleton& Singleton::operator=(const Singleton &) {}
    42 43 Singleton *Singleton::GetInstance()
    44 {
    45     if (nullptr == s_instance)
    46     {
    47         lock();
    48         if(nullptr == s_instance)
    49         {
    50             s_instance = new Singleton();
    51         }
    52         unlock();
    53     }
    54 55     return s_instance;
    56 }

    三、Reference

    C++单例(知乎):https://zhuanlan.zhihu.com/p/37469260

    C++单例(博客园):https://www.cnblogs.com/leaves1024/p/10985599.html

    盘点C++几种常见的设计模式及具体实现:https://zhuanlan.zhihu.com/p/321939154

  • 相关阅读:
    隔壁小孩都要知道的Drupal配置
    Cobaltstrike、armitage联动
    YxCMS 1.4.7 最新版漏洞分析
    业务逻辑漏洞探索之暴力破解
    刺透内网的HTTP代理
    pwn入门之栈溢出练习
    史上最完整的MySQL注入
    ISG 2018 Web Writeup
    3-4 计算长方形的周长和面积
    【Lucene4.8教程之二】索引
  • 原文地址:https://www.cnblogs.com/horacle/p/15439078.html
Copyright © 2020-2023  润新知