设计模式之单例模式的几种写法

来源:互联网 发布:改短信号码软件 编辑:程序博客网 时间:2024/05/16 01:06

前言

经过了前面几次的面试,我发现面试官对设计模式方面的提问频率是非常高的,所以学习设计模式是一件刻不容缓的事情,今天我们就来说一下设计模式中的单例模式吧。

什么是设计模式?

设计模式并不是什么神技,而是一套代码的设计,是经验的总结,使用设计模式是为了实现代码的复用以及“解耦”,关于编程里面解耦的概念可以参考一下下面这篇知乎。
https://www.zhihu.com/question/20821697?sort=created


设计模式的原则

这里话不多少,给出一片不错的博客,大家可以去欣赏一下:
http://blog.jobbole.com/101076/


什么是单例模式

顾名思义,单例模式的含义就和它的名字一样,就是一个类型只能实例化一个对象出来,并且这个对象对整个系统来说是可见的,也就是说我们要提供一个全局访问点,向整个系统提供这个唯一的实例。


单例模式的实现(C++)

好了,经过了前面的叙述,我们现在可以写下代码了,这里我们用C++来实现单例模式,语言不重要,思路才是关键。

class Singleton1{public:    static Singleton1* GetInstance()    {        if (single == NULL)        {            single = new Singleton();        }        return single;    }private:    Singleton1()     //注意构造函数是私有的    {}private:    static Singleton1* single;};Singleton1* Singleton1:: single=NULL;

这段代码实现了单例模式的关键点有两个:
1.保护
我们将类的构造函数声明成非公有的,这样就可以防止通过类的成员函数来在任何地方实例化对象,当然,我这里写的不是很严格,我们不光要对构造函数进行保护,我们还应该对拷贝构造函数以及赋值运算符重载等类的默认函数也提供保护。

2.类的成员变量申明为static类型:
这一点是很关键的,因为单例模式的设计中有一个必要的原则就是类的实例对象只能有一个,而且类的实体对整个系统都要提供接口来进行访问,使用静态变量可以满足上面的要求,因为静态变量只要对象已创建它就一直存在,知道程序结束完才被销毁。


大家应该注意到我写的类名为Singleton1了吧,因为仅仅这样实现是不够的,还需要考虑一些其他的因素,例如多线程环境时运行上面的代码就会出现问题,例如当第一个线程运行到if (single == NULL) 的时候被系统切了出去,此时第二个线程也运行到了这句话,但是此时对象没有被创建,所以single的状态也就没有发生变化,线程2会创建一个对象,当回到线程1时,由于判断条件已经执行过了,所以线程1也会创建这个对象,这就导致在一个进程中有两个对象,违背了单例模式的设计原则。

上述问题的发生是因为线程安全性的问题所导致的,我们可以引入互斥锁来解决这个问题,因此在多线程的环境下,单例模式的代码可以这样来写:

std::mutex g_lock;class Singleton{public:    static Singleton* GetInstance()    {        g_lock.lock();        if (single == NULL)        {            single = new Singleton();        }        g_lock.unlock();        return single;    }private:    Singleton()     //注意构造函数是私有的    {}private:    static Singleton* single;};Singleton* Singleton:: single=NULL;

我们通过引入互斥锁来维护了上面提到的线程安全之间的问题,但是各个线程反复的申请锁是很影响性能的,所以我们还可以在此基础上再增加一层判断条件,当single为NULL时才允许申请锁资源。

std::mutex g_lock;class Singleton{public:    static Singleton* GetInstance()    {        if (single == NULL)        {            g_lock.lock();            if (single == NULL)            {                single = new Singleton();            }            g_lock.unlock();        }        return single;    }private:    Singleton()     //注意构造函数是私有的    {}private:    static Singleton* single;};Singleton* Singleton:: single=NULL;

好了,代码在这里就基本实现完了,我们现在可以再来回顾一下单例模式。

单例模式和全局变量的区别

通过观察前面的代码,我们可以发现单例模式的作用其实就像全局变量一样,可以说是全部变量的替代品,它拥有全部变量的特性,例如生命周期等。但此外,它还有一些全部变量所不具有的特性,单例模式中,同一个类型的实例对象只能有一个,此外,单例模式还提供了延迟初始化,以提高程序的性能。

0 0