设计模式
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。使用设计模式的目的是为了代码可重用性、让代码更容易被他人理去解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
单例模式
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。
饿汉模式
饿汉模式就是在程序运行之前将所有的资源一次性创建好,在之后用的时候就会很方便。由于静态的变量是在进入主函数之前就创建好了,所以要定义一个静态变量,然后将构造函数和拷贝构造设置为私有的,拷贝构造函数要只声明不定义,并在类中给出一个创建对象的静态方法,这个静态方法只能是引用返回,如果值返回,就会调用拷贝构造,但是拷贝构造调不了,即使可以调就会创建一个对象,这样就和单例模式相冲突。虽然这种方式在程序运行之后不用再去创建资源,直接调用就可以,但是如果这份资源很大,那么程序就会启动的很慢。
class Singleton
{
public:static Singleton* GetInstance(){return &p;}private://构造函数私有Singleton(){}//防止被拷贝Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;
private:static Singleton p;
};Singleton Singleton::p;
使用场景
在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞
争,提高响应速度更好。
懒汉模式
实现过程:
懒汉模式就是在需要的时候才会去创建 构造函数和拷贝构造函数都要设为私有的,同样的在类中给出一个创建静态的方法,并且在类中创建一个静态的指针,在调用这个方法时如果这个静态的指针是空的,就为这个指针new一个对象,返回去。如果是在多线程的环境中,就会出现多个线程同时去申请资源,这时候就要多线程中常用的加解锁操作,如果每次都要先加锁在检测,这时候所有的线程都会阻塞在加锁这里,等解锁后才可以继续操作,所以使用DCL双检锁,这时如果有一个线程在进行加解锁操作,另一个线程也过来了,这时候资源已经申请好了,这个后来的线程就可以直接返回,不用阻塞等待之前的线程。但是这样的版本仍然是有问题的,如果线程A正在进行对象的的创建,线程B过来了,但是A现在只是申请了空间,还没有进行实例化,但是B检测到这个对象不为空,直接返回去使用,就会出问题,所以要将静态对象加一个volatile来限定一下,告诉系统每次取变量里面的信息的时候不要从寄存器中取,要到内存里面取,这样就禁止了编译器对创建对象的次序进行优化(申请空间->构造对象->赋值—>申请空间->赋值->构造对象)。虽然改进了这么多,但是这个代码还存在问题,那就是没有释放空间,可能会存在内存泄漏。如果要释放,就要保证所有的线程已经用完了这份资源,但是不能在类中直接给出一个静态的释放函数,这样有可能会忘记调用这个函数,最好的方法就是内嵌一个内部类来实现释放。
#include <mutex>
#include <thread>using namespace std;class Singleton1
{
public:volatile Singleton1* GetInstance(){if (p == nullptr)//要采用DCL双检锁,让其他的线程可以不用等,直接返回。//这样如果编译器对代码进行了优化,将创建对象的顺序重新调整,直接返回就会出错。{m_tex.lock();//如果这里只加这一个锁,然后去判断,其他线程会阻塞在这里等待解锁。if (p == nullptr)p = new Singleton1;m_tex.unlock();}return p;}//在释放资源的时候要保证所有线程已经将这份资源用完,但是不能直接在类中给出一个释放资源的函数,有可能忘记调用这个函数//最好的方法是在类中内嵌一个类负责资源释放class Clean{public:~Clean(){if (Singleton1::p){delete Singleton1::p;Singleton1::p = nullptr;}}};static Clean c;private:Singleton1(){}Singleton1(const Singleton1&) = delete;Singleton1& operator=(const Singleton1&) = delete;
private:static Singleton1 volatile *p;static mutex m_tex;
};
//为对象添加volatile关键字,告诉系统取变量里面的信息的时候从内从中取,这样就禁止了编译器对变量的创建顺序进行优化
//但是这样还不够,就是没有释放空间,会造成内存泄漏。
volatile Singleton1* Singleton1::p = nullptr;
mutex Singleton1::m_tex;
Singleton1::Clean c;
使用场景
如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。