设计模式 --> (2)单例模式
单例模式
单例模式也称为单件模式、单子模式,可能是使用最广泛的设计模式。其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。如系统的日志输出,GUI应用必须是单鼠标,MODEM的联接需要一条且只需要一条电话线,操作系统只能有一个窗口管理器,一台PC连一个键盘。单例模式有两种:饿汉式单例类和懒汉式单例类。
适用性:
单例模式常常与工厂模式结合使用,因为工厂只需要创建产品实例就可以了,在多线程的环境下也不会造成任何的冲突,因此只需要一个工厂实例就可以了。
优点:
1.减少了时间和空间的开销(new实例的开销)。
2.提高了封装性,使得外部不易改动实例。
缺点:
1.懒汉式是以时间换空间的方式。
2.饿汉式是以空间换时间的方式。
饿汉式单例类
//外部初始化 before invoke main const Singleton* Singleton::m_instance = new Singleton; class Singleton { private: static const Singleton* m_instance; Singleton(){} public: static const Singleton* getInstance() { return m_instance; } };
懒汉式单例类
class Singleton { private: static Singleton* m_instance; Singleton() {} public: static Singleton* getInstance(); }; Singleton* Singleton::getInstance() { if(NULL == m_instance) { m_instance = new Singleton; } return m_instance; }
但是在多线程的环境下却不行了,因为很可能两个线程同时运行到if (m_instance == NULL)这一句,导致可能会产生两个实例。于是就要在代码中加锁。
Singleton* getInstance() { lock(); if (instance == NULL) { instance= new Singleton(); } unlock(); return instance; }
但这样写的话,会稍稍影响性能,因为每次判断是否为空都需要被锁定,如果有很多线程的话,就爱会造成大量线程的阻塞。于是大神们又想出了双重锁定。
Singleton* getInstance() { if (instance == NULL) { lock(); if (instance == NULL) { instance = new Singleton(); } unlock(); } return instance; }
这样只够极低的几率下,通过越过了if (instance == NULL)的线程才会有进入锁定临界区的可能性,这种几率还是比较低的,不会阻塞太多的线程,但为了防止一个线程进入临界区创建实例,另外的线程也进去临 界区创建实例,又加上了一道防御if (instance == NULL),这样就确保不会重复创建了。
使用
#include <iostream> using namespace std; // 单例模式 class Singleton { private: Singleton() {}; // 不允许直接构造其对象 static Singleton *instance; public: static Singleton* createInstance() { if(!instance) { // 对象第一次被创建,允许 cout << "创建新对象" << endl; instance = new Singleton(); } else { // 请求再次创建对象,不允许 cout << "已经创建过对象了,返回原对象" << endl; } return instance; } void getAddress() { cout << "我的地址是 " << instance << endl; } }; Singleton* Singleton::instance = 0; //在初始化的时候,不能在前面加static了 int main() { //Singleton s;//报错:无法访问 private 成员(在“Singleton”类中声明) Singleton *s1 = Singleton::createInstance(); s1->getAddress(); cout << endl << endl; Singleton *s2 = Singleton::createInstance(); s2->getAddress(); return 0; }