设计模式:
设计模式是一套被反复使用,多数人知晓的,经过分类的,代码设计经验的总结。
使用设计模式的目的:为了代码可重用性,让代码更容易被他人理解,保证代码可靠性。设计模式使代码编写真正工程化;设计模式是软件工程的基础脉络,如同大厦的结构一样。
单例模式:
一个类只能创建一个对象,既单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件 中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置 信息,这种方式简化了在复杂环境下的配置管理。
单例模式有两种实现模式
1.饿汉模式
饿汉模式就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象
代码实现
class Singleton
{
public:
static Singleton* GetInstance()
{
return &m_instance;
}
private:
//构造函数私有
Singleton()
{};
//防止拷贝
Singleton(Singleton const&);
//C++11 delete删除函数
Singleton& operator=(Singleton const&s1) = delete;
static Singleton m_instance;
};
Singleton Singleton::m_instance;//在程序入口前就完成单例对象的初始化
int main()
{
//Singleton s1;在定义变量m_instance时就已经创建了对象,再次创建对象时会失败
//Singleton s(*Singleton::GetInstance());用拷贝构造创建对象也会失败,因为拷贝函数被声明为私有的
//Singleton s1 = *Singleton::GetInstance();运算符重载也会创建失败,因为运算符重载并声明为已删除函数并且还是私有的
Singleton::GetInstance();
return 0;
}
如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。
2.懒汉模式
懒汉模式就是需要实例,实例要被用到的时候才会被加载
如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好
代码实现
#include <iostream>
using namespace std;
class Singleton
{
public:
static Singleton* GetInstance()
{
if (m_instance == nullptr)
{
m_instance = new Singleton();
}
return m_instance;
}
private:
Singleton();//构造函数私有化,保证其他地方不能直接创建对象
//防止拷贝
Singleton(const Singleton&);
//C++11 delete删除函数
Singleton &operator = (const Singleton& s1) = delete;
static Singleton* m_instance;
};
Singleton *Singleton::m_instance = nullptr;
int main()
{
Singleton::GetInstance();
return 0;
}
但是如果在类析构行为有必须的操作,例如要关闭文件,释放外部资源,那么上面的代码无法实现这个要求。因为这个指向对象的指针是一个静态成员,无法调用本类的析构函数
这里有两种方法,正常的删除该实例
1.可以在程序结束时调用Getinstance(),并对返回的指针掉用delete操作。但是这样做容易出错。因为很难保证在delete之后,没有代码再调用GetInstance函数。
2.让这个类把删除自己的操作挂在操作系统中的某个合适的点上,使其在恰当的时候被自动执行。我们知道程序在结束的时候,系统会自动析构所有的全局变量和所有的类的静态成员变量。利用这个特征,我们可以在单例类中定义一个静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。
#include<iostream>
using namespace std;
class Singleton
{
public:
static Singleton* Getinstance()
{
if (m_instance == nullptr)
{
m_instance = new Singleton();
}
return m_instance;
}
void Print()
{
cout << "懒汉模式" << endl;
}
//定义一个内部垃圾回收类,它是外部类的友元类,可以访问外部类的成员
class CGarbo
{
public:
~CGarbo()
{
if (Singleton::m_instance)
delete Singleton::m_instance;
}
void Print1()
{
cout << "析构函数" << endl;
}
//定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象
static CGarbo Garbo;
};
private:
Singleton()//构造函数私有
{}
//防止拷贝
Singleton1(const Singleton1& sl);
//C++11 delete删除函数
Singleton1& operator=(const Singleton1& sl) = delete;
static Singleton* m_instance;
};
Singleton* Singleton::m_instance = nullptr;
Singleton::CGarbo Garbo;
int main()
{
Singleton::Getinstance()->Print();
return 0;
}
上述的类可以很好的解决释放的问题。但是依然存在很多问题,例如线程安全的问题,如果两个线程同时走到new的时候,指针m_instance只有一份,而两个线程都会new一份空间,那么这两块空间应该怎么分配给这个指针呢?这就导致了线程安全的问题。
我们可以用互斥锁和双检查来解决这个问题:
#include<iostream>
using namespace std;
#include <mutex> //互斥量
class Singleton
{
public:
static Singleton* GetInstance()
{
//double check(可以提高效率,如果没有双检查,原本并行的线程到这里都变成串行的)
if (m_instance == nullptr)
{
m_mtx.lock();//相当于二元信号量,只允许一个人进去
if (m_instance == nullptr)
m_instance = new Singleton;
m_mtx.unlock();
}
return m_instance;
}
void Print()
{
cout << "懒汉模式" << endl;
}
//实现一个垃圾回收内部类
class CGarbo
{
public:
~CGarbo()
{
cout << "删除" << endl;
}
//定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象
static CGarbo Garbo;
};
private:
Singleton()//构造函数私有
{}
//防止拷贝
Singleton(const Singleton&);
//C++11 delete删除函数
Singleton& operator=(const Singleton&) = delete;
static Singleton* m_instance;//单列对象指针
static mutex m_mtx; //互斥锁
};
mutex Singleton::m_mtx;
Singleton::CGarbo Garbo;
Singleton* Singleton::m_instance = nullptr;
int main()
{
Singleton::GetInstance()->Print();
return 0;
}