设计模式--单例模式

134 阅读2分钟

单例模式顾名思义,保证一个类仅可以有一个实例化对象,并且提供一个可以访问它的全局接口。实现单例模式必须注意一下几点:

  • 单例类只能由一个实例化对象。
  • 单例类必须自己提供一个实例化对象。
  • 单例类必须提供一个可以访问唯一实例化对象的接口。

单例模式分为懒汉和饿汉两种实现方式。

饿汉单例模式

饿汉式是直接创建出类的实例化,比较简单粗暴。

简单来说就是空间换时间,因为上来就实例化一个对象,占用了内存,(也不管你用还是不用)

#include <iostream>
using namespace std;

class Singleton
{
public:
    static Singleton* getInstance();
private:
    Singleton(){}
    static Singleton* p_singleton;
};

Singleton* Singleton::p_singleton = new Singleton();
Singleton* Singleton::getInstance()
{
    return p_singleton;
}

int main()
{
    Singleton *s1 = Singleton::getInstance();
    Singleton *s2 = Singleton::getInstance();
    cout << (s1 == s2) << endl;
    return 0;
}

上述代码实现了一个饿汉的单例模式,执行结果是1,即s1 和 s2是相等的。

懒汉单例模式

对于懒汉式,就是在getInstance方法中增加一个if判断,判断当前对象是否为null,如果为null,则创建实例,比饿汉式多了一个判断。

简单的来说就是时间换空间,与饿汉式正好相反

非线程安全的懒汉单例模式

#include <iostream>
using namespace std;

class Singleton
{
public:
    static Singleton* getInstance();
private:
    Singleton(){} //make construct private

    static Singleton* p_singleton;
};

Singleton* Singleton::p_singleton = NULL;
Singleton* Singleton::getInstance()
{
    if (p_singleton == NULL)
    {
        p_singleton = new Singleton();
    }
    return p_singleton;
}


int main()
{
    Singleton *s1 = Singleton::getInstance();
    Singleton *s2 = Singleton::getInstance();
    cout << (s1 == s2) << endl;
    return 0;
}

上述例子的输出,是1,证明s1和s2是相等的,但是如果多线程情况下,p_singleton在被创建之前,另外一个线程也进行了if判断,这样就会出现问题,所以需要有线程安全的单例模式。

线程安全的懒汉单例模式

#include <iostream>
#include <mutex>
using namespace std;

std::mutex mt;

class Singleton
{
public:
    static Singleton* getInstance();
private:
    Singleton(){} //make construct private

    static Singleton* p_singleton;
};

Singleton* Singleton::p_singleton = NULL;
Singleton* Singleton::getInstance()
{
    mt.lock();
    if (p_singleton == NULL)
    {
        p_singleton = new Singleton();
    }
    return p_singleton;
    mt.unlock();
}


int main()
{
    Singleton *s1 = Singleton::getInstance();
    Singleton *s2 = Singleton::getInstance();
    cout << (s1 == s2) << endl;
    return 0;
}

所谓的线程安全,就是在判断之前增加了锁的机制,确保当前无人使用。

总结

至于懒汉和饿汉哪个更好,就看使用的时候需要的时间还是空间。