不能是栈对象
只能是堆对象,且在类内部
只要出现全局对象的地方都可以使用单例模式
取代全局变量的位置
全局唯一的资源或只需要出现一个
配置文件、网页库、字典库、日志系统写的日志对象
配置文件的信息是程序的输入
内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
避免对资源的多重占用(比如写文件操作)
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
将构造函数私有化
这样该类就不会被实例化
例
定义一个私有的静态数据成员指针,指向本类型的对象,即单例对象
初始化本类型静态数据成员指针
定义一个静态的成员函数getInstance方法用于获取实例
静态成员函数无需对象也可调用
如果把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符 :: 就可以访问。
例
static Singleton * getInstance()
{
if(_pInstance == nullptr) {
_pInstance = new Singleton();
}
return _pInstance;
}
若本类型还未创建过对象,new一个本类型对象,若已经存在本类型对象,返回本类型对象指针
饿汉模式就是立即加载,一般情况下在调用getInstance 方法之前就已经产生了实例,也就是在类加载的时候已经产生了一个单里对象了。
饿汉模式是线程安全的
初始化
Singleton * Singleton::instance = new Singleton();
什么时候需要这个单例对象时,再去创建实例。
if(instance == nullptr)
{
instance = new Singleton();
}
这两行代码既包含了读操作,也包含了修改操作,这两行代码是两个不走,不是原子性的。此代码存在线程安全问题
改进方案:在多线程下运行时,需要进行加锁操作(最终实现)
class Singleton {
public:
static Singleton * getInstance(){
if(instance == nullptr) {
pthread_mutex_lock(&mutex);//需要在数据成员内加入mutex
if(instance == nullptr) {
instance = new Singleton();
}
pthread_mutex_unlock(&mutex);
}
return instance;
}
static void destroy()
{
if(_pInstance) {
//在类内部执行delete表达式
delete _pInstance;
}
}
private:
Singleton();
~Singleton();
static pthread_mutex_t mutex;
static Singleton * instance;
};
初始化
Singleton * Singleton::instance = nullptr;
程序需要检查是否存在内存泄漏,如果单例对象没有进行内存回收,那么检测工具会认为发生了内存泄漏,会干扰程序员判断是否发生内存泄漏
检测内存泄漏的工具valgrind
使用方式
valgrind --tool=memcheck --leak-check=fukk ./test(可执行文件)
valgrind --tool=massif --stack=yes ./client
单例对象声明与实现
class AutoRelease; //前向声明
class Singleton
{
friend AutoRelease;
public:
static Singleton * getInstance();
private:
Singleton();
~Singleton();
private:
static Singleton * _pInstance;
};
SIngleton * Singleton::getInstance()
{
if(nullptr == _pInstace)
{
_pInstance = new Singleton;
}
return _pInstance;
}
自动释放类声明与实现
class AutoRelease
{
public:
AutoRelease()
{
cout << "AutoRelease()" << endl;
}
~AutoRelease()
{
cout << "~AutoRelease()" << endl;
if(Singleton::_pInstance)
{
delete Singleton::_pInstance;
Singleton:_pInstance = nullptr;
}
}
};
可自动释放的单例对象实现
//静态成员初始化
Singleton * Singleton::_pInstance = nullptr;
void test0()
{
Singleton *p1 = Singleton::getInstance();
//自动释放实现需要声明一个AutoRelease类
AutoRelease p2;
}
单例对象的声明
//单例对象自动释放的实现方式之一: 嵌套类+静态对象
class Singleton
{
public:
static Singleton * getInstance()
{
if(nullptr == _pInstance)
_pInstance == new Singleton();
return _pInstance;
}
private:
class AutoRelease
{
public:
~AutoRelease()
{
cout << "~AutoRelease()" << endl;
if(_pInstance)
delete _pInstance; //内部类可以直接使用外部类的数据成员
}
};
private:
Singleton() { cout << "Singleton()" << endl; }
~Singleton() { cout << "~Singleton()" << endl;}
static SIngleton * _pInstance;
static AutoRelease _ar; //_ar是静态对象,位于静态存储区
//当程序退出main函数时会自动回收
//分配给单例对象的内存
int _ix;
};
ar时AutoRelease类静态对象,位于全局静态区, pInstance是外部Singleton类静态对象指针,也位于全局静态区
因此当程序退出main函数时,会delete _at的同时会调用析构函数
从而可以自动回收分配给_pInstance 的内存空间
静态成员的初始化
Singleton * Singleton::_pInstance = nullptr;
Singleton::AutoRelease Singleton::_ar;
类调用
int main()
{
Singleton * p1 = Singleton::getInstance();
Singleton * p2 = SIngleton::getInstance();
cout << "p1 = " << p1 << endl;
cout << "p2 = " << p2 << endl;
return 0;
}
atexit函数
函数说明
用来注册指向exit() 函数前执行的终止处理程序
当程序通过调用exit()或从main中返回时被调用。终止处理程序执行的顺序和注册时的顺序时相反的,终止处理程序没有参数传递。同一个函数若注册多次,那它也会被调用多次。
当一个子进程时通过调用fork函数产生时,它将继承父进程的所有终止处理程序。在成功调用exec系列函数后,所有的终止处理程序都会被删除
函数原型
#include
int atexit(void (*function)(void));
参数列表
function
即所要注册的终止处理函数指针
使用实例
#include
using std::cout;
using std::endl;
class Singleton
{
public:
static Singleton * getInstance()
{
//对于多线程环境,不安全
if(nullptr == _pInstance)
{
_pInstance = new Singleton();
atexit(destroy);
}
return _pInstance;
}
static void destroy()
{
if(_pInstance)
{
delete _pInstance; //1、调用析构函数 2、operator delele
_pInstance = nullptr;
}
}
private:
Singleton()
{
cout << "Singleton()" << endl;
}
~Singleton()
{
cout << "~Singleton()" << endl;
}
private:
static Singleton * _pInstance;
};
/* Singleton *Singleton::_pInstance = nullptr; //饱汉模式(懒汉模式)*/
Singleton * Singleton::_pInstance = getInstance(); //饿汉模式
pthread_once 函数
函数说明
在多线程环境中,有些事仅需要执行一次。通常当初始化应用程序时,可以比较容易地将其放在main函数中。但当你写一个库时,就不能在main里面初始化了,你可以用静态初始化,但使用一次初始化(pthread_once)会比较容易些
函数功能
本函数使用初值为PTHREAD_ONCE_INIT的once_control变量保证init_routine()函数在本进程执行序列中仅执行一次
函数原型
int pthread_once(pthread_once_t * once_control,
void(*init_routine)(void));
参数列表
once_control
表示是否执行过,必须初始化为PTHREad_ONCE_INIT
init_routine
初始化函数
使用实例
#include
using std::cout;
using std::endl;
class Singleton
{
public:
static Singleton * getInstance();
static void init();
static void destroy();
private:
Singleton()
{
cout << "Singleton()" << endl;
}
~Singleton()
{
cout << "~Singlenton()" << endl;
}
private:
static Singleton * _pInstance;
static pthread_once_t _once;
};
Singleton * Singleton::getInstance() {
pthread_once(&_once, init);
return _pInstance;
}
void Singleton::init()
{
_pInstance = new Singleton();
atexit(destroy);
}
void Singleton::destroy()
{
if(_pInstance)
{
delete _pInstance;
_pInstance = nullptr;
}
}
Singleton * Singleton::_pInstance = nullptr; //饱汉模式
// Singleton * Singleton::_pInstance = Singleton::getInstance();//饿汉模式
pthread_once_t Singleton::_once = PTHREAD_ONCE_INIT;