C++实现单例模式

来源:互联网 发布:如何做好网络咨询 编辑:程序博客网 时间:2024/06/06 15:40

C++实现单例模式

第一次写关于技术的文章,选择了一个单例模式这样的例子作为开头。用自己的理解去实现,希望能够表达清楚。

懒汉式

class Singleton{    private:        Singleton(){}        static Singleton* instance;    public:        static Singleton* getInstance()        {            if(NULL == instance)            {                instance = new Singleton();            }            return instance;        }};

//静态成员变量必须在实现文件中进行定义,而不仅仅是在头文件中声明

Singleton* Singleton::instance = NULL;

这个代码简单,但是会存在内存泄漏的问题,new出来的对象始终没有释放,下面是它的一种改进。

class Singleton{    private:        Singleton() {}        static Singleton* m_pInstance;        //嵌套类        class CGrabo        {            public:            ~CGrabo()            {                if(Singleton::m_pInstance)                {                    delete Singleton::m_pInstance;                    Singleton::m_pInstance = NULL;                }            }        };        static CGrabo Grabo;    public:        static Singleton* getInstance()        {            if(NULL == m_pInstance)            {                m_pInstance = new Singleton();            }            return m_pInstance;        }};

说明:在程序运行结束的时候,静态成员变量Grabo的析构函数会被调用,该析构函数会删除单例中的唯一实例。其特点是在单例类的内部定义专有的内部类,并且定义该类的一个私有静态成员,利用静态变量的特性,选择最终的释放时机。

饿汉式

class Singleton{    private:        Singleton() {}    public:        static Singleton * getInstance()        {            static Singleton instance;            return &instance;        }};

饿汉模式实现简单并且是线程安全的,在类创建的同事就已经创建好一个静态的对象供系统使用。

懒汉模式在多线程中会出现线程安全的问题,因为在懒汉模式中单例实例有两种状态,分别是初始化和未初始化。假设单例实例还没有初始化,有两个线程同事调用getInstance方法,这是执行m_pInstance==NULL肯定为真,然后两个线程都初始化一个实例,最后得到的指针并不是指向同一个地方,不满足单例的定义了。在多线程环境下,要对其进行修改。

多线程下的单例模式

——————————— 懒汉模式

class Singleton{    private:        static Singleton* m_pInstance;        Singleton(){}    //嵌套类    class CGrabo    {        public:        ~CGrabo()        {            if(Singleton::m_pInstance)            {                delete Singleton::m_pInstance;                Singleton::m_pInstance = NULL;            }        }    };    static CGrabo Grabo;    public:        static Singleton* getInstance();};

在实现类实现

Singleton* Singleton::getInstance(){    if(NULL == m_pInstance)    {        Lock();//通过同步类来实现,如临界区,事件,互斥,信号量等        if(NULL == m_pInstance)        {            m_pInstance = new Singleton();        }        UnLock();    }    return m_pInstance;}

这里为什么需要两次判断m_pInstance??这是为了防止一个线程进入临界区创建实例,另外的线程也进入临界区创建实例,所以加上第二个判断if(NULL==m_pInstance),确保不会重复创建。

如果是这样写是否也可以???

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

这样写的话,会稍微影响性能,因为每次判断是否为空都需要被锁定,如果有很多线程的话,就会造成大量线程阻塞。

既然如此,也会有人想能不能把同步锁放入到判断空里面,这样就不会每次都锁了,像这样:

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

这样就又回到了饿汉模式的非线程安全上去了。

这是总结的一些东西,或许并不是总结得很好,也会有很多自己没有想到的东西,或者还有更加巧妙的方法来改进,这里暂时就不再深究了。

0 0