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