c++:设计模式之单例模式(懒汉模式、饿汉模式)(一)

1、什么是单例模式?

单例模式:一个类只能创建一个对象,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。

实现单例模式的思路:

1.屏蔽构造函数
2.提供接口生成唯一对象

应用场景:比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。其他还有如系统的日志输出、MODEM的联接需要一条且只需要一条电话线,操作系统只能有一个窗口管理器,一台PC连一个键盘等等。


根据单例对象创建时间,可分为两种模式:

懒汉模式  延时加载

饿汉模式  贪婪加载

一、懒汉模式

特点:延迟加载,时间换空间 .第一次使用实例对象时,创建对象。进程启动无负载。多个单例实例启动顺序自由控制。

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

应用场景:如果单例对象构造十分耗时或者占用很多资源,比如配置文件,加载插件, 初始化网络连接,读取文件等,有可能在该对象程序运行时不会用到,如果在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况最好使用懒汉模式。

1.线程不安全的单例模式

class Singleton
{
private:
    Singleton(){}
public:
    static Singleton* Instance();
private:
    static Singleton* ptr; 
};
Singleton* Singleton::ptr = NULL; 
Singleton* Singleton::Instance() 
{
    if (ptr == NULL)
    {
        ptr = new Singleton;
    }
    return ptr;
}

该方法是线程不安全的,考虑两个线程同时首次调用getInstance方法就不止生成一个对象。考虑两个线程同时首次调用Instance方法且同时检测到ptr是NULL值,则两个线程会同时构造一个实例给ptr。

2.双重锁线程安全版

class SingleTon
{
public:
	static SingleTon* getInstance()
	{
		if (psingle == NULL)
		{
			lock();
			if (psingle == NULL)
			{
				psingle = new SingleTon();
			}
			unlock();
		}
		return psingle;
	}
private:
	SingleTon(){}
	SingleTon(const SingleTon&);
	static SingleTon* psingle;
};
SingleTon* SingleTon::psingle = NULL;
int main()
{
	return 0;
}

思路解释:

  1. 首先构造函数私有,为了防止别人随便创建对象,同时要进行防拷贝的工作,将拷贝构造函数和赋值运算符重载私有,C++98可以只声明不实现这两个函数,C++11里可以使用delete删除这两个函数。
  2. 在编译期间不能消耗太多资源,因此在编译的时候只创建一个指针,创建指针的消耗并不大
  3. 提供接口,在该接口内部就要用new来创建该对象,因此就是psingle = new SingleTon();
  4. 但是如果这样的话每次调用这个公有的接口就会产生一个对象,所以此时要加判断条件,如果指针为空则创建对象,如果指针不为空则返回这个对象的指针。
  5. 如上基本完成了单例,但是有个很严重的问题就是如果有多个线程来调用该接口的话,并且多个线程同时来访问,这样还是会创建出多个对象。为了线程安全,这就需要加锁,加锁的话可以让并行的线程变成串行来执行,这样就可以保证这个公有的接口每次只能由一个线程来调度,之后的线程就会被挂起等待,直到前面的线程将锁释放。
  6. 还有一个小的效率问题,就是如果线程很多的话,每个线程过来都要申请锁释放锁就会非常的影响效率,所以这里就用到了双重检查,再给外层加一条判断语句,保证后序再来的线程发现该指针为空就直接返回,不用再进行加锁解锁的操作了,这样就提高了整个程序的效率。

二、饿汉模式

 特点:空间换时间

饿汉模式:在单例类定义的时候就进行实例化。因为main函数执行之前,全局作用域的类成员静态变量m_Instance已经初始化,故没有多线程的问题。

缺点:因为对象的创建初始化都是在编译期间完成的,因此可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定。

class SingleTon
{
public:
	static SingleTon* getInstance()
	{
		return psingle;
	}
private:
	SingleTon(){}
	SingleTon(const SingleTon&);
	static SingleTon* psingle;
};
SingleTon* SingleTon::psingle = new SingleTon();

特点与选择:
在访问量较小时,采用懒汉实现,这是以时间换空间。
在访问量比较大,采用饿汉实现,这是以空间换时间。

这个博主提供了两种懒汉模式的实现方法,可以借鉴参考哈~

用C++实现单例模式1——懒汉模式和饿汉式

 

你可能感兴趣的:(c++,设计模式,线程安全,空间换时间,时间换空间)