1 优点
- 提供了对唯一实例的受控访问。
- 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
- 允许可变数目的实例。
2 缺点
- 由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
- 单例类的职责过重,在一定程度上违背了“单一职责原则”。
- 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
- 对象生命周期。单例模式没有提出对象的销毁,在提供内存的管理的开发语言中,只有单例模式对象自己才能将对象实例销毁,因为只有它拥有对实例的引用。在各种开发语言中,比如C++,其他类可以销毁对象实例,但是这么做将导致单例类内部的指针指向不明。
3 代码示例
懒汉模式:
class LogMannage
{
public:static LogMannage* instance(); void A();
private:static LogMannage *myLog; LogMannage(); ~LogMannage();
};
LogMannage* LogMannage::myLog = NULL; LogMannage *LogMannage::instance()
{if(myLog == NULL) mPtr = new ThreadWifiBase();return mPtr;
}
饿汉模式:
class LogMannage
{
public:static LogMannage* instance(); void A();
private:static LogMannage *myLog; LogMannage(); ~LogMannage();
};
LogMannage* LogMannage::myLog = new LogMannage(); LogMannage *LogMannage::instance()
{return myLog;
}
4 单例模式实现原理
1.1 私有化构造函数,使得外界不能通过new方式创建该单例对象。
1.2 创建静态对象指针。
1.1、1.2操作代码如下所示:
class RaspiTemp
{
private:static RaspiTemp *instancePrt; explicit RaspiTemp(); ~RaspiTemp();
}
1.3 单例对象访问接口定义:
需要定义为静态函数,可供外部直接访问而不需要依据对象指针访问。代码如下:
public:static RaspiTemp* instance();
1.3 单例对象的创建
懒汉模式是在需要用的时候,即首次访问时才创建单例对象,所以首次访问相对比较慢。懒汉模式就好像一个懒人(狠人),一直懒得找对象,在需要结婚的时候才去找对象,有了对象后就结婚;而对应的饿汉就是,管他结婚不结婚,先找个对象再说,需要结婚的时候直接结就行了。
饿汉模式是在程序启动,静态变量(1.2中单例对象类的静态指针)初始化的时候就创建单例对象。由于静态变量初始化在代码导入内存时候就初始化了,即main函数运行之前就创建了单例对象,所以不存在线程安全问题。
1.3.1 懒汉模式实现
代码如下:
RaspiTemp *RaspiTemp::instancePrt = NULL;
RaspiTemp*RaspiTemp::instance()
{if(instancePrt== NULL) instancePrt= new RaspiTemp(); return instancePrt;
}
1.3.1 饿汉模式实现
代码如下:
RaspiTemp *RaspiTemp::instancePrt = new RaspiTemp();
RaspiTemp*RaspiTemp::instance()
{return instancePrt;
}
5 单例模式的线程安全实现
前面有说明,饿汉模式在静态变量初始化的时候就实例化了单例对象,所以不存在线程安全问题,而且其访问接口中只有返回对象指针而没有创建对象的代码。但是单例模式对象的创建是在首次访问的时候,如果两个线程同时访问的时候,就可能造成单例对象被重复构建而导致内存浪费(对!仅仅内存浪费而已),解决此方法就是使用互斥锁。
为了理解,还是使用之前例子说明一下,结婚前才找对象,其实就是相亲,在你没相亲成功前你就是单身状态,你可以同时相亲两个或多个妹子(帅哥),不巧结果全相中了,这时候你的对象就有了多个,而结婚的对象确只能一个(别问我为什么,我也不知道),于是其他对象就被放弃在那里;然后政府为了避免这种浪费可耻的现象,于是规定你在同一时刻只能相亲一个对象,这个对象相亲结束后才能相亲下一个,这样就能解决问题了不是,但是怎么做到呢?这就是需要上锁,首先判断指针是否为空(指向对象),如果为空就加锁,然后再判断一次后再创建对象。
懒汉模式中代码修改如下:
static QMutex staticMutexSend;
RaspiTemp *RaspiTemp::instance()
{if(instancePrt == NULL) {staticMutexSend.lock(); if(instancePrt == NULL)instancePrt = new RaspiTemp ();staticMutexSend.unlock(); }return instancePrt ;
}
6 单例模式+独立子线程模式
多线程的实现,qt有两种方式,一种是继承Qthread类并重写run() 函数实现;一种是继承QObject类,使用QObject中的moveToThread() 函数实现。