C++实现单例模式

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值