单例模式

单例模式产生的原因:

每个类都能产生很多对象,而在某些场景下,我们仅需要一个实例,所有的操作都针对这个单例来进行。

实际构建中,我们需要保证一个类仅有一个实例,并提供一个访问它的全局访问点。

常用两种:

懒汉式:

class single
{
public:
    ~single()
    {
        if(mInstance)
        {
            delete mInstance;
        }
    };
    static single* getInstance()
    {
        if(mInstance == NULL)
        {
            mInstance = new single();
        }
        return mInstance;
    }
    void printArr()
    {
        printf("%p  ",this);
    }
private:
    single(){};
    single(const single&);
    single& operator = (const single&);
    static single* mInstance;
};
single* single::mInstance = NULL;
void main()
{
    single::getInstance()->printArr();
    single::getInstance()->printArr();
    cin.get();
}

得到结果: 00785D70  00785D70

我们的到这个实例后任何的操作都是基于这单个的对象进行。

先讲一下为什么是static的数据成员mInstance和static的函数成员getInstance

是为了把这个对象存放在全局的存储区内,供所有需要调用的地方调用。

另外补充一下,静态数据成员(非静态常量成员)不能在类中初始化,因为静态数据成员为所有的该类对象所共有,没有必要在每个类构建对象时去重新赋值,这与所有该类维护同一份static变量本身也是违背的。

另外为了保证单例的唯一性,我把它的构造函数定义在了private部分,另外将它的拷贝构造函数和赋值函数也声明为私有,这样避免了除了getIntance()得到类的对象其他所有通过隐式构造产生对象的可能。

这种懒汉式的单例模式在单线程中是毫无问题的,但在多线程中仍存在问题,那就是两个线程可能同时判断为NULL,这样就new了两个对象。

饿汉式:

class single
{
public:
    ~single()
    {
        if(mInstance)
        {
            delete mInstance;
        }
    };
    static single* getInstance()
    {
        return mInstance;
    }
    void printArr()
    {
        printf("%p  ",this);
    }
private:
    single(){};
    single(const single&);
    single& operator = (const single&);
    static single* mInstance;
};
single* single::mInstance = new single();

在程序启动时即去new这个实例的指针,后续调用时也就不存在同时为NULL的这种情景。懒汉式虽然存在线程安全的问题,确可以节省空间。工作中为了避免线程安全问题和懒得加锁,一般还是饿汉式的省心一点。不过在linux平台上,跑的时候偶尔发现main函数调用到

getInstance()了,全局外部的new还没有new出来导致使用了空指针,至今还没搞明白,可能cpp文件比较多,初始化跑的顺序比单个文件复杂一些。

因此在此基础上还有一种加锁的懒汉式单例写法:

方式一:两次判空,getInstance()中,第一次判空的时候加锁,第二次判空new 对象,new 完之后解锁

方式二:利用静态变量只初始化一次的特性构建单例,懒得写了,都差不多,copy一份

这里仍然要注意的是局部变量初始化的线程安全性问题,在C++0X以后,要求编译器保证静态变量初始化的线程安全性,可以不加锁。但C++ 0X以前,仍需要加锁。

class Singleton
{
public:
    static Singleton* GetInstance()
    {
        Lock(); // not needed after C++0x 
        static Singleton instance;  
        UnLock(); // not needed after C++0x 

        return &instance;
    }

private:
    Singleton() {};
    Singleton(const Singleton &);
    Singleton & operator = (const Singleton &);
};
其中lock()为线程锁锁,UnLock为线程锁解锁,两个线程同时在跑时,当一边lock住时,另一线程将处于等待状态,unlock时,结束等待,这样就避免了同时new这种情况的产生

单例是各种设计模式中经常用到的且非常好用的类对象的调用模式。


posted @ 2018-11-30 19:30  落叶满空山  阅读(117)  评论(0编辑  收藏  举报