单例模式

单例模式(SingletonPattern)是一个比较简单的模式,其定义为确保一个类只有一个实例,而且自行实例化并向整个系统提供这个示例。类的使用者一般都是通过构造函数来生成实例,如果将构造函数设为私有成员,那么类的使用者就不能随便构造实例了。

单利模式中这唯一的一个实例一般是以静态成员的身份出现在类中的,当然类就得提供一个静态的公有接口来初始化并向使用者返回这个实例。

单例模式结构图


单例模式的C++实现:

class Singleton{
private:
    static Singleton *singleton;
private:
    Singleton(){
    };
public:
    static Singleton* GetInstance(){
        if(Singleton==NULL)
            singleton = new Singleton();
        return Singleton;
    };
    void sayHello(){
        cout<<"Hello World"<<endl;
    }
};
Singleton * Singleton::singleton;		//定义静态成员变量

int main()
{
    Singleton *temp;
    temp=Singleton::GetInstance();
    temp->sayHello();
    return 0;
}

Tips : C++的静态成员变量必须在类的外部定义,在类里面的只是声明,并没有申请空间。若没有在类的外部定义,则编译时会报找不到符号定义的错误。

上述代码虽然实现了简单的单例模式,但存在几个问题:

1.  这种实现在多线程中存在线程同步的问题,当多个线程中同时执行GetInstance函数时,结果是不可预料的,解决这个问题的办法也很简单,就是加锁。

2.    如果注意观察上面的代码中只出现了new关键字,没有delete关键字,这符合内存管理中谁申请谁释放的原则,会造成内存泄漏,当然值得庆幸的是我们这里是单例模式,从而保证了整个进程中不会有多个此类的对象,我们完全可以交由操作系统在进程退出的时候来回收这些内存(由于singleton是个指针,并不是个对象,析构函数是不会调用的,这样并不安全),如果这个类的实例占用内存比较大,且在整个进程生存期里很少用到,那么最好还是在不用它的时候将其释放掉;如果在类的析构行为中有必须的操作,比如关闭文件,释放外部资源,那么上面的代码无法实现这个要求。

 

改进后的单例模式:

#include <iostream>
#include <Windows.h>

using namespace std;
class Lock
{
public:
    Lock(void){InitializeCriticalSection(&m_lock);};
    ~Lock(void){DeleteCriticalSection(&m_lock);};
    void RequireLock(){EnterCriticalSection(&m_lock);};
    void ReleaseLock(){LeaveCriticalSection(&m_lock);};
private:
    CRITICAL_SECTION m_lock;
};//使用资源管理类来托管内核资源,这样即使发生异常,也能保证内核资源的正常释放(在析构函数中释放)。

class Singleton{
private:
    static Singleton *singleton;
    static Lock lock;
    int test;
private:
    Singleton(){
        test = 5;
    };
public:
    static Singleton* GetInstance(){
        if(Singleton == NULL)	//多一次判断是为了防止示例生成以后每次执行这个
        {						//函数还要不断的加锁,加锁是个很费时间的操作。
            lock.RequireLock();
            if(Singleton == NULL)
                singleton = new Singleton();
            lock.ReleaseLock();
        }
        return Singleton;
    };
    void sayHello(){
        cout<<"Hello World"<<"\ttest is "<<test<<endl;
    }
    void Release(){
        if(Singleton != NULL)
        {
            delete singleton;
            Singleton=NULL;
        }
    }
};
Singleton * Singleton::singleton; 		//定义静态成员变量
Lock Singleton::lock;

int main()
{
    Singleton *temp;
    temp=Singleton::GetInstance();
    temp->sayHello();
    temp->Release();
//  temp=NULL;			注释掉此句后会发生指针悬垂的问题。
    temp->sayHello();
    return 0;
}

执行结果:

Tips : 把资源放在对象中,可以依赖C++的“析构函数自动调用的机制”确保资源被释放,即使在异常发生的情况下。

由使用者手动调用Release函数类析构上述对象是不完美的,因为总有人会因为各种原因而忘记Release函数的调用,如果能实现Singleton的析构函数的自动调用就没有这个问题了。

我们知道,程序在结束的时候,系统会自动析构所有的全局变量。事实上,系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。上面的singleton成员变量确实是个静态成员变量,但可惜它是个指针而不是个对象,就不存在自动析构的机会了。一个巧妙的办法就是在类中在添加一个特殊类的静态对象做成员变量,这个类中只实现了析构函数,而在其析构函数中只负责调用Singleton的析构函数,这样就能实现Singleton的唯一实例的自动析构。

进一步改进后的Singleton类

class Singleton{
private:
    static Singleton *singleton;
    static Lock lock;
    int test;
private:
    Singleton(){
        test = 5;
    };
    class Garbo   //它的唯一工作就是在析构函数中删除CSingleton的实例  
    {  
    public:  
        ~Garbo()  
        {  
            if(Singleton::singleton)  
                delete Singleton::singleton;
        }  
    };  
    static Garbo garbo;  //定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数  
public:
    static Singleton* GetInstance();
    void sayHello();
    ~Singleton();
    //void Release();
};
Singleton * Singleton::singleton;
Lock Singleton::lock;
Garbo Singleton::garbo; 

Garbo的意思是垃圾工人,即负责回收工作,很形象的名字吧!这样做唯一的缺点就是使Singleton的唯一实例的生存周期延长至整个进程的生存周期结束。有得必有失嘛!




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值