单例设计模式
2020 年 08 月 20 日 530 749 字 暂无评论

01.概念

  • 单例模式是一种对象创建型模式,使用单例模式,可以保证为一个类只生成唯一的实例对象。也就是说,在整个程序空间中,该类只存在一个实例对象。
  • 保证一个类、只有一个实例存在,同时提供能对该实例加以访问的全局访问方法。

简单的说法

  • 单例模式,顾名思义,只能由一个实例,该类不能被复制,该类不能被公开创造。
  • 该类的构造函数,拷贝函数和他的赋值函数都不能被公开调用。

02.为什么使用单例模式

  • 在多个线程之间,比如初始化一次socket资源;比如servlet环境,共享同一个资源或者操作同一个对象。
  • 在整个程序空间使用全局变量,共享资源。
  • 大规模系统中,为了性能的考虑,需要节省对象的创建时间等等。
  • 因为Singleton模式可以保证为一个类只生成唯一的实例对象,所以这些情况,Singleton模式就派上用场了。

03.实现单例的步骤

  1. 构造函数私有化。
  2. 提供一个全局的静态方法(全局访问点)。
  3. 在类中定义一个静态指针,指向本类的变量的静态变量指针 。

04.单例的几种模式和代码实现

4.1懒汉模式

  • 只有当需要改类的时候去构造。
#include <iostream>

using namespace std;

class Singelton
{
private://1、构造函数私有化
        //C++都构造函数,不能保证是线程安全
        Singelton()
        {
                cout << "Singelton start" << endl;
        }
public: //2、提供全局访问点
        static Singelton* GetSingelton() 
        {
                //每一次GetSingelton时候,都需要判断single是否存在
                if (single == NULL)
                {
                        single = new Singelton; //多线程时、线程1、线程2、都会调构造函数
                }
                return single;
        }
private:
        static Singelton* single;
};

//静态变量指针赋值
Singelton* Singelton::single = NULL;

void main()
{
        //只有我们调用GetSingelton时候,类才会new出对象,
        //在new对象实例的时候做判断 ==》 比较懒 懒汉式
        Singelton* s1 = Singelton::GetSingelton();
        Singelton* s2 = Singelton::GetSingelton();
        if (s1 == s2)
        {
                cout << "s1==s2" << endl;
        }
        else
        {
                cout << "s1!=s2" << endl;
        }
}
  • 输出
Singelton start
s1==s2

4.2饿汉模式

  • 在程序开始之前我们就先构造好,到时候直接用。
#include <iostream>

using namespace std;

class Singelton
{
private://1、构造函数私有化
        //C++都构造函数,不能保证是线程安全
        Singelton()
        {
                cout << "Singelton start" << endl;
        }
public: //2、提供全局访问点
        static Singelton* GetSingelton()
        {
                return single;
        }

private:
        static Singelton* single;
};

//静态变量指针赋值
Singelton* Singelton::single = new Singelton;

void main()
{
        //只有我们调用GetSingelton时候,类才会new出对象,
        //在new对象实例的时候做判断 ==》 比较懒 懒汉式
        Singelton* s1 = Singelton::GetSingelton();
        Singelton* s2 = Singelton::GetSingelton();
        if (s1 == s2)
        {
                cout << "s1==s2" << endl;
        }
        else
        {
                cout << "s1!=s2" << endl;
        }
}
  • 输出
Singelton start
s1==s2

4.3多线程下懒汉式单例的Double-Checked Locking优化

  • 线程安全的问题,当多线程获取单例时有可能引发竞态条件
    • 第一个线程在if中判断 single是空的,于是开始实例化单例。
    • 同时第2个线程也尝试获取单例,这个时候判断single还是空的,于是也开始实例化单例;这样就会实例化出两个对象,这就是线程安全问题的由来。
    • 解决办法:加锁
  • 内存泄漏
    • 注意到类中只负责new出对象,却没有负责delete对象,因此只有构造函数被调用,析构函数却没有被调用;因此会导致内存泄漏。
    • 解决办法: 使用共享指针
#include <iostream>
#include <memory> // shared_ptr
#include <mutex>  // mutex

// version 2:
// with problems below fixed:
// 1. thread is safe now
// 2. memory doesn't leak

class Singleton {
public:
    typedef std::shared_ptr<Singleton> Ptr;
    ~Singleton() {
        std::cout << "destructor called!" << std::endl;
    }
    Singleton(Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    static Ptr get_instance() {

        // "double checked lock"
        if (m_instance_ptr == nullptr) {
            std::lock_guard<std::mutex> lk(m_mutex);
            if (m_instance_ptr == nullptr) {
                m_instance_ptr = std::shared_ptr<Singleton>(new Singleton);
            }
        }
        return m_instance_ptr;
    }


private:
    Singleton() {
        std::cout << "constructor called!" << std::endl;
    }
    static Ptr m_instance_ptr;
    static std::mutex m_mutex;
};

// initialization static variables out of class
Singleton::Ptr Singleton::m_instance_ptr = nullptr;
std::mutex Singleton::m_mutex;

int main() {
    Singleton::Ptr instance = Singleton::get_instance();
    Singleton::Ptr instance2 = Singleton::get_instance();
    return 0;
}
  • 输出
constructor called!
destructor called!

没有了,点个赞吧。


评论已关闭