C++设计模式——Singleton模式

来源:互联网 发布:网络 克隆 编辑:程序博客网 时间:2024/06/06 08:23

一、什么是单例模式:

何为单例模式,在GOF的《设计模式:可复用面向对象软件的基础》中是这样说的:保证一个类只有一个实例,并提供一个访问它的全局访问点。首先,需要保证一个类只有一个实例;在类中,要构造一个实例,就必须调用类的构造函数,如此,为了防止在外部调用类的构造函数而构造实例,需要将构造函数的访问权限标记为protected或private;最后,需要提供要给全局访问点,就需要在类中定义一个static函数,返回在类内部唯一构造的实例。

二、单例模式的应用

单例模式:用来创建独一无二的,只能够有一个实例的对象。 单例模式的结构是设计模式中最简单的,但是想要完全实现一个线程安全的单例模式还是有很多陷阱的。单例模式的应用场景:有一些对象其实只需要一个,比如:线程池,缓存,对话框,处理偏好设置和注册表的对象,日志对象,充当打印机,显卡等设备的驱动程序对象。这些对象只能够拥有一个实例,如果创建出了多个实例,就会导致一些程序的问题。程序的行为异常,资源使用的过量,或者导致不一致的结果。常用来管理共享的资源,比如数据库的连接或者线程池。

三、代码实现(c++):

方式一(经典懒汉实现):

#pragma onceclass Singleton{public:    static Singleton* Instance();    ~Singleton();private:    Singleton();    static Singleton* _instance;};
#include "stdafx.h"#include<iostream>#include "Singleton.h"Singleton* Singleton::_instance = nullptr;Singleton::Singleton(){}Singleton * Singleton::Instance(){    if(nullptr==_instance)    {        _instance = new Singleton();    }    return _instance;}Singleton::~Singleton(){}
#include "stdafx.h"#include"Singleton.h"int main(){    Singleton* sgt = Singleton::Instance();    return 0;}

这是最简单,也是最普遍的实现方式。但是,这种实现方式,有很多问题,比如:没有考虑到多线程的问题,在多线程的情况下,就可能创建多个Singleton实例。(可以想一下在多线程情况下,产生多个Singleton实例的问题,结合C++对象模型,对象内存布局来思考?)
以下版本是改善的版本。

方式二(线程安全懒汉实现):

#pragma once#include<mutex>class Singleton{public:    static Singleton* Instance();    ~Singleton();private:    Singleton();    static Singleton* _instance;    static std::mutex _mutex;};
#include "stdafx.h"#include<iostream>#include "Singleton.h"Singleton* Singleton::_instance = nullptr;std::mutex Singleton::_mutex;Singleton::Singleton(){}Singleton * Singleton::Instance(){    if(nullptr == _instance)    {        _mutex.lock();        if(nullptr==_instance)        {            _instance = new Singleton();        }        _mutex.unlock();    }    return _instance;}Singleton::~Singleton(){}
#include "stdafx.h"#include"Singleton.h"int main(){    Singleton* sgt = Singleton::Instance();    return 0;}

此处进行了两次_instance == nullptr的判断,使用的所谓的“双检锁”机制。因为进行一次加锁和解锁是需要付出对应的代价的,而进行两次判断,就可以避免多次加锁与解锁操作,同时也保证了线程安全。但是,这种实现方法在平时的项目开发中用的很好,也没有什么问题?但是应当注意加锁操作将成为一个性能的瓶颈;为此,一种新的单例模式的实现也就出现了。

方式三(饿汉实现):

#pragma onceclass Singleton{public:    static Singleton* Instance();    ~Singleton();private:    Singleton();    static Singleton* _instance;};
#include "stdafx.h"#include<iostream>#include "Singleton.h"Singleton* Singleton::_instance = new Singleton();Singleton::Singleton(){}Singleton * Singleton::Instance(){    return _instance;}Singleton::~Singleton(){}
#include "stdafx.h"#include"Singleton.h"int main(){    Singleton* sgt = Singleton::Instance();    return 0;}

因为静态初始化在程序开始时,也就是进入主函数之前,由主线程以单线程方式完成了初始化,所以静态初始化实例保证了线程安全性。在性能要求比较高时,就可以使用这种方式,从而避免频繁的加锁和解锁造成的资源浪费。

方式四(静态局部变量懒汉实现):

#pragma onceclass Singleton{public:    static Singleton* Instance();    ~Singleton();private:    Singleton();};
#include "stdafx.h"#include<iostream>#include "Singleton.h"Singleton::Singleton(){}Singleton * Singleton::Instance(){    static Singleton _instance;    return &_instance;}Singleton::~Singleton(){}
#include "stdafx.h"#include"Singleton.h"int main(){    Singleton* sgt = Singleton::Instance();    return 0;}

其实是使用了C++中成员函数的静态变量的特点:静态局部变量在第一次使用时初始化,并不会销毁直到程序退出。

三、关于懒汉饿汉

可以这样简单粗暴的理解:

  • 懒汉式的特点是延迟加载,比如配置文件,采用懒汉式的方法,顾名思义,懒汉么,很懒的,配置文件的实例直到用到的时候才会加载。*
  • 饿汉式的特点是一开始就加载了,如果说懒汉式是“时间换空间”,那么饿汉式就是“空间换时间”,因为一开始就创建了实例,所以每次用到的之后直接返回就好了。
0 0
原创粉丝点击