(二)singleton单例模式——懒汉模式

只能生成一个实例的类是实现了单例模式的类。

singleton是唯一一个能够用短短几十行代码完整实现的模式。

懒汉模式:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化。

懒汉式是典型的时间换空间,也就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间。调用效率低,可以延时加载。

一、懒汉模式(有缺陷):

#include "../common.h"

//final表示该类不可被继承
class CMySingleton final
{
public:
    ~CMySingleton();
    static CMySingleton* getInstance(char *pData);
private:
    CMySingleton(char *pData);
    char *m_data;
    static CMySingleton *pInstance; 
};

CMySingleton::CMySingleton(char *pData)
{
    printf("This is Constructors (data:%s) ...\n",pData);
}
CMySingleton::~CMySingleton() 
{
    printf("This is Destructor  ...\n");
    if(m_data != nullptr)
    {
        delete []m_data;
        m_data = nullptr;
    }
    /*绝对不能在析构函数里面释放类对象,不然会一直递归运行!!!
    if(pInstance != nullptr)
    {
        delete pInstance;
        pInstance = nullptr;
    }*/
}

CMySingleton *CMySingleton::getInstance(char *pData)
{
    if(pInstance == nullptr)
    {
        pInstance = new CMySingleton(pData);
    }
    return pInstance;   
}

CMySingleton *CMySingleton::pInstance = nullptr;

void *pthCreate(void*)
{
    printf("into pthCreate() ... \n");
    CMySingleton *singleton_1 = CMySingleton :: getInstance(nullptr);

}
int main()
{
    char data[] = "abcdefg";
    printf("----------------1-----------------\n");
    {
        CMySingleton *singleton_1 = CMySingleton :: getInstance(data);
        CMySingleton *singleton_2 = CMySingleton :: getInstance(data);
        delete singleton_1;  //如果不确定是否会再次使用单例类,就不要delete,不会自动析构
        //delete singleton_2; //delete除了执行析构函数,还会释放掉对象本身的空间,多次释放会导致double free。
    }
 /*   printf("-----------------2----------------\n");
    { 
        pthread_t pid1 = 0, pid2 = 0;
        pthread_create(&pid1,NULL,pthCreate,NULL);
        pthread_create(&pid2,NULL,pthCreate,NULL);
        sleep(100); //防止线程还未创建进程就结束
    }*/
}

使用第一段代码时,结果:

----------------1-----------------
This is Constructors (data:abcdefg) ...
This is Destructor  ...

可以看出只实例化了一个对象。

使用第二段代码时,结果:

-----------------2----------------
into pthCreate() ... 
into pthCreate() ... 
This is Constructors (data:(null)) ...
This is Constructors (data:(null)) ...

可以看到构造函数运行了两次,这是因为多线程有可能引发竞态条件。

上边的类中只负责new对象,却没有delete对象,会不会有内存泄漏?

  答:一般情况下,单例类的实例都是常驻内存的,一直存在于进程的生命周期,因此不需要手动释放。如果的确需要释放实例占用的内存,一定不能在单例类的析构函数中进行delete操作,这样会造成无限循环。

 

为了解决上面两个问题,我们可以修改程序:

二、懒汉模式(无缺陷):

#include "../common.h"
#include <mutex> 
//final表示该类不可被继承
class CMySingleton final
{
public:
    typedef std::shared_ptr<CMySingleton> CMySingletonPtr;  //智能指针
    ~CMySingleton();
    static CMySingletonPtr getInstance(char *pData);
    CMySingleton(CMySingleton&)=delete;
    CMySingleton& operator=(const CMySingleton&)=delete;
private:
    CMySingleton(char *pData);
    char *m_data;
    static CMySingletonPtr pInstance; 
    static std::mutex tMutex;
};

CMySingleton::CMySingleton(char *pData)
{
    printf("This is Constructors (data:%s) ...\n",pData);
}
CMySingleton::~CMySingleton() 
{
    printf("This is Destructor  ...\n");
    if(m_data != nullptr)
    {
        delete []m_data;
        m_data = nullptr;
    }
}

CMySingleton::CMySingletonPtr CMySingleton::getInstance(char *pData)
{
    lock_guard<mutex> lock(tMutex);
    if(pInstance == nullptr)
    {
        pInstance = CMySingletonPtr(new CMySingleton(pData));
    }
    return pInstance;   
}

CMySingleton::CMySingletonPtr CMySingleton::pInstance = nullptr;
std::mutex CMySingleton::tMutex;

void *pthCreate(void*)
{
    printf("into pthCreate() ... \n");
    CMySingleton::CMySingletonPtr singleton_1 = CMySingleton :: getInstance(nullptr);

}
int main()
{
    char data[] = "abcdefg";
    /*printf("----------------1-----------------\n");
    {
        CMySingleton::CMySingletonPtr singleton_1 = CMySingleton :: getInstance(data);
        CMySingleton::CMySingletonPtr singleton_2 = CMySingleton :: getInstance(data);
    }*/
    printf("-----------------2----------------\n");
    { 
        pthread_t pid1 = 0, pid2 = 0;
        pthread_create(&pid1,NULL,pthCreate,NULL);
        pthread_create(&pid2,NULL,pthCreate,NULL);
        sleep(100); //防止线程还未创建进程就结束
    }
}

通过加锁解决了多线程问题,运行第二段代码段结果:

-----------------2----------------
into pthCreate() ... 
into pthCreate() ... 
This is Constructors (data:(null)) ...

可以看到只运行了一次构造函数

接下来运行第一段代码:

----------------1-----------------
This is Constructors (data:abcdefg) ...
This is Destructor  ...

可以看到程序结束前自动调用析构函数。

三、懒汉模式——局部静态变量

#include "../common.h"
//final表示该类不可被继承
class CMySingleton final
{
public:
    ~CMySingleton();
    static CMySingleton &getInstance(char *pData);
    CMySingleton(CMySingleton &) = delete;
    CMySingleton &operator=(const CMySingleton &) = delete;

private:
    CMySingleton(char *pData);
    char *m_data;
};

CMySingleton::CMySingleton(char *pData)
{
    printf("This is Constructors (data:%s) ...\n", pData);
}
CMySingleton::~CMySingleton()
{
    printf("This is Destructor  ...\n");
    if (m_data != nullptr)
    {
        delete[] m_data;
        m_data = nullptr;
    }
}

CMySingleton &CMySingleton::getInstance(char *pData)
{
    //lock()如果编译器不支持,还是只能加锁解锁
    static CMySingleton pInstance(pData);
    return pInstance;
}

void *pthCreate(void *)
{
    printf("into pthCreate() ... \n");
    CMySingleton &singleton_1 = CMySingleton ::getInstance(nullptr);
}
int main()
{
    char data[] = "abcdefg";
    /*printf("----------------1-----------------\n");
    {
        CMySingleton &singleton_1 = CMySingleton :: getInstance(data);
        CMySingleton &singleton_2 = CMySingleton :: getInstance(data);
    }*/
    printf("-----------------2----------------\n");
    {
        pthread_t pid1 = 0, pid2 = 0;
        pthread_create(&pid1, NULL, pthCreate, NULL);
        pthread_create(&pid2, NULL, pthCreate, NULL);
        sleep(100); //防止线程还未创建进程就结束
    }
}

结果:

----------------1-----------------
This is Constructors (data:abcdefg) ...
This is Destructor  ...
-----------------2----------------
into pthCreate() ... 
This is Constructors (data:(null)) ...
into pthCreate() ... 

它的效果与无缺陷版是一样的,但是代码更加简洁、简单。

这种方法又叫做 Meyers' SingletonMeyer's的单例, 是著名的写出《Effective C++》系列书籍的作者 Meyers 提出的。所用到的特性是在C++11标准中的Magic Static特性:

If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.
如果当变量在初始化的时候,并发同时进入声明语句,并发线程将会阻塞等待初始化结束。

这样保证了并发线程在获取静态局部变量的时候一定是初始化过的,所以具有线程安全性。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值