单例设计模式(懒汉模式饿汉模式)
Posted It‘s so simple
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例设计模式(懒汉模式饿汉模式)相关的知识,希望对你有一定的参考价值。
1 设计一个类,只能在堆上开辟空间
在正常情况下,用类实例化出的对象,都会调动它的构造函数,构造函数会在栈上申请一份空间用来存储该对象的成员变量,但是现在规定该类只能在堆上开辟空间,对应的解决方案就是将该类的构造函数声明为私有的(private),为了能正常的获取到该类在堆上产生的对象,我们可以定义一个静态的方法,用来返回在堆上创建的对象。
- 将该类的构造和拷贝构造函数声明为私有,防止别人调用拷贝在栈上产生对象。
- 给定一个静态方法,在该静态方法中实现在堆上开辟空间。
代码实现:
class heapOnly{
public:
static heapOnly* GetObject()
{
return new heapOnly;
}
private:
heapOnly()
{}
//注意这里一定要用到delete方法来表示对该方法进行删除
//这样做的目的是防止友元函数对该类拷贝构造的调用,从而导致在栈上开辟空间
heapOnly(const heapOnly& h) = delete;
};
2 设计一个类,只能在栈上开辟空间
实现方法:
① 由于new关键字的底层是调用系统全局函数 operator new 实现的,因此我们只需要将operator new 函数声明为私有的即可,并且一旦我们将operator new 声明为私有时,对应的operator delete也要声明为私有的
代码实现:
class StackOnly1{
public:
StackOnly1()
{}
private:
void* operator new(size_t size);
void operator delete(void* p);
};
② 和1.1一样,我们将构造函数声明为私有的,然后用静态方法返回对象。
代码实现:
class StackOnly2{
public:
static StackOnly2 GetObject()
{
return StackOnly2();
}
private:
StackOnly2()
{}
};
3. 设计一个类,只能创建一个对象(单例模式)
设计模式:
设计模式就是一套可以反复使用的、很多人都知晓的、经过分类的,代码设计经验的总结。使用设计模式的目的就是为了代码的可重用性,让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
单例模式:
一个类只能创建一个对象,即单例模式,在该模式下可以保证整个系统中该类均只有一个实例,并提供一个访问它的全局的访问点,该实例被所有程序所共享。
单例模式的应用场景:
在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置中的信息,这种方式简化了在复杂环境下的配置管理。
并且单例模式由两种实现模式:饿汉模式和懒汉模式
① 饿汉模式:
就是不管将来用不用,程序在启动时就会创建出唯一的一个实例对象
代码实现:
class singleton
{
public:
static singleton* GetObject()
{
//注意我们要返回该静态对象的指针,不能返回它的对象
//因为该类的拷贝构造和赋值重载均被定义为私有的,并进行了删除声明
return &sg;
}
private:
singleton()
{}
singleton(const singleton &e) = delete;
singleton operator=(const singleton &e) = delete;
static singleton sg;
};
//在程序入口之前就完成该对象的初始化
singleton singleton::sg;
饿汉模式的优点就是简单,而缺点则是可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定。
如果这个单例对象在多线程高并发的环境下频繁调用,性能要求比较高,那么显然使用饿汉模式来避免资源竞争,提升响应速度更好。
②懒汉模式:
如果单例对象构造十分耗费时间,比如加载插件、初始化网络连接、读取文件等等,但是有可能该对象程序在运行时是不会用到的,那么在程序一开始运行就进行初始化,就会导致程序启动时非常缓慢。因此这种情况下使用懒汉模式更好。
代码实现:
#include <iostream>
#include <mutex>
using namespace std;
class singleton{
public:
static singleton* GetObject()
{
//双检锁
//第一个if是用来提升性能的
//第二个if是用来保证线程安全的
if(psg == nullptr)
{
mt.lock();
if(psg == nullptr)
{
psg = new singleton;
}
mt.unlock();
}
return psg;
}
//我们在这里定义一个回收类,专门用来回收我们在堆上申请的空间
class Recycle{
public:
~Recycle()
{
if(psg)
delete singleton::psg;
}
};
//由于懒汉模式下只会产生一个对象,因此当程序结束时,
//rc再调用析构函数的时候就会对我们动态申请的psg空间进行释放。
static Recycle rc;
private:
singleton()
{}
singleton(const singleton& sg) = delete;
singleton& operator=(const singleton& sg) = delete;
//这样这里不能用该静态的对象,因为这是懒汉模式,
//只有在需要用到它的时候才会创建对象
static singleton* psg;
//加上互斥锁变量是为了保证线程安全
static mutex mt;
};
singleton* singleton::psg = nullptr;
mutex singleton::mt;
singleton::Recycle rc;
懒汉模式的优点就是只有在第一次使用该实例对象时才会创建对象,进程启动是无负载的,多个单例实例的启动顺序是自由控制的,但是缺点也有就是复杂。
以上是关于单例设计模式(懒汉模式饿汉模式)的主要内容,如果未能解决你的问题,请参考以下文章