(一)单例模式的设计目的
单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点;
(1)基于全局访问点的思想,很容易想到定义全局或者静态对象,但是这样不能保证该类只声明一个静态对象,并没有实现仅有一个对象的思想;
(2)单例模式设计方式
- 将类的构造函数写在私有下;
- 定义一个静态的指针指向该类唯一的对象;
- 定义一个公有下返回该对象的静态方法
代码如下:
//单例模式
class CSingleton
{
private:
CSingleton(){}//构造写在私有下
static CSingleton *m_pInstance;//定义该类的静态指针指向该类的唯一实例化对象
public:
static CSingleton *GetInstance ()//通过静态成员方法获得静态指针所指向的对象。
{
if(m_pInstance == NULL)
{
m_pInstance = new CSingleton();
}
return m_pInstance;//指针指向唯一的实例化对象
}
};
上述代码实现了单例模式,但是其在多线程下不安全,下图中模拟了一种线程不安全的情景:
可以从图中看到if 中的代码相当于临界区,所以访问临界区资源时需要进行同步控制,故而我们引入了锁机制,代码如下:
//单例模式
class CSingleton
{
private:
CSingleton(){}//构造写在私有下
static CSingleton *m_pInstance;//定义该类的静态指针指向该类的唯一实例化对象
public:
static CSingleton *GetInstance ()//通过静态成员方法获得静态指针所指向的对象。
{
lock();
if(m_pInstance == NULL)
{
m_pInstance = new CSingleton();
}
unlock();
return m_pInstance;//指针指向唯一的实例化对象
}
};
分析上述代码:
(1)首先A线程执行代码,拿到资源先加锁,判断m_pInstance是否为空,如果不为空则构造对象;直到实例化对象后解锁
(2)B线程同时在执行代码时发现已被其他线程加锁,则不进入,只有解锁后,B判断m_pInstance已经不为空,则不进行构造;
这样解决了线程安全问题,接下来引入一种双重安全控制代码,这样保证了如果此对象已经被构造,则此线程就不再访问到临界区,这样更安全,代码如下:
//单例模式
class CSingleton
{
private:
CSingleton(){}//构造写在私有下
static CSingleton *m_pInstance;//定义该类的静态指针指向该类的唯一实例化对象
public:
static CSingleton *GetInstance ()//通过静态成员方法获得静态指针所指向的对象。
{
if((m_pInstance == NULL)//在外层控制线程是否要接触到临界资源
{
//如果为空则加锁构造对象;
//如果不为空则接触到临界资源
lock();
if(m_pInstance == NULL)
{
m_pInstance = new CSingleton();
}
unlock();
}
return m_pInstance;//指针指向唯一的实例化对象
}
};
(3)什么时候释放合适呢?我们可以在其私有下设计一个内嵌类,专门用来释放程序运行结束后对象所占用的资源,必须写在私有下的原因是防止其文件的调用类内析构函数释放对象。代码如下:
class CSingleton
{
private:
CSingleton(){}//构造写在私有下
static CSingleton *m_pInstance;//定义该类的静态指针指向该类的唯一实例化对象
//写在私有下 专门用来析构对象所占资源
class A_gorb
{
public:
~A_gorb()
{
delete CSingleton::m_pInstance;
}
};
static A_gorb a_g;
public:
static CSingleton *GetInstance ()//通过静态成员方法获得静态指针所指向的对象。
{
if((m_pInstance == NULL)//在外层控制线程是否要接触到临界资源
{
//如果为空则加锁构造对象;
//如果不为空则接触到临界资源
lock();
if(m_pInstance == NULL)
{
m_pInstance = new CSingleton();
}
unlock();
}
return m_pInstance;//指针指向唯一的实例化对象
}
};
(4)单例模式的使拥实例
- 打印机:只要有打印任务在进行,就必须等待当前打印任务结束在打印,一个PC端只能在同一时刻连接一台打印机;
- 系统的日志文件:只需要一个日志文件记录系统的状态信息
- 网站的计数器,如果你存在多个计数器,每一个用户的访问都刷新计数器的值,这样的话你的实计数的值是难以同步的。但是如果采用单例模式实现就不会存在这样的问题,而且还可以避免线程安全问题
(5)应用场景特点总结
-
需要生成唯一序列的环境
-
需要频繁实例化然后销毁的对象。
-
创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
-
方便资源相互通信的环境
版权声明:本文为weixin_41966991原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。