一、什么是单例
单例模式是设计模式的一种,其特点是只提供唯一一个类的实例,具有全局变量的特点,在任何位置都可以通过接口获取到的唯一实例;
使用场景
- 如果系统中只有唯一一个实例存在的类的全局变量的时候才使用单例。
- 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
- 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
- windows的Recycle Bin(回收站)也是典型的单例应用,在整个系统运行过程中,回收站一直维护着仅有的一个实例。
- 对单例的要求:越简单越小越好,线程安全,内存不泄露
■ 1.1 单例模式分类
单例模式可以分为懒汉式和饿汉式,区别在于创建实例的阶段不同:
懒汉式(线程不安全):程序运行时,当需要使用该实例时,才创建并使用实例。
懒汉,从名字上来看就知道,懒么,到了要用到的时候再去;
饿汉式(线程安全):程序启动时就创建实例并初始化,当需要时直接调用即可。
饿汉式相对于懒汉式来说,就勤快了,不需要等到需要的时候催着去做,自己在一开始就做好准备了;
■ 1.2 单例类特点
构造函数和析构函数为private类型,禁止外部构造和析构
拷贝构造和赋值构造函数为private类型,目的是禁止外部拷贝和赋值,确保实例的唯一性类中有一个获取实例的静态函数getInstance,可以全局调用
实现思路
在c++中,创建一个对象,首先会先调用类的构造函数,那么,实现单例就是要把构造函数私有化,然后给外界提供一个接口用来返回这个类的实例对象,现在只要让接口每一次返回的都是同一个实例对象就行了,那么这个实例对象就应该被创建为一个静态对象,保存静态数据区。
二、 实现
2.1 基础要点
- 局只有一个实例:static 特性,同时禁止用户自己声明并定义实例
- 线程安全
- 禁止赋值和拷贝
- 用户通过接口获取实例:使用 static 类成员函数
原因如下:
-
通过静态的类方法(getInstance) 获取instance,该方法是静态方法,instance由该方法返回(被该方法使用),如果instance非静态,无法被getInstance调用;
-
instance需要在调用getInstance时候被初始化,只有static的成员才能在没有创建对象时进行初始化。并且类的静态成员在类第一次被使用时初始化后就不会再被初始化,保证了单例。
-
static类型的instance存在静态存储区,每次调用时,都指向的同一个对象。
2.2 C++ 实现单例的几种方式
- 懒汉式
- 饿汉式
- 模板类
饿汉式在平时一般不会使用,懒汉式会有进程不安全的因素,在使用中往往会引入智能指针和锁来进行保护,这里只提供懒汉式和饿汉式的基本写法。
2.2.1 懒汉式单例
懒汉式:直到使用时才实例化对象,直到调用getInstance() 方法的时候才 new 一个单例的对象。优点是如果不被调用就不会占用内存。
lazy_single.h
/**
* @file lazy single design
* @datetime 2022-06-03
* @version v0.0.1
*/
#include <iostream>
using namespace std;
/**
* @class lazy_single
* @datetime 2022-06-03
* @version v0.0.1
*/
class lazy_single
{
public:
~lazy_single( )
{
cout << "This is < lazy_single > destructor!" << endl;
}
static lazy_single *getInstance()
{
if( !m_pInstance )
{
m_pInstance = new lazy_single;
}
return m_pInstance;
}
private:
lazy_single( )
{
cout << "This is < lazy_single > constructor!" << endl;
}
lazy_single( lazy_single& ) = delete;
lazy_single& operator = ( const lazy_single& ) = delete;
static lazy_single *m_pInstance;
};
lazy_single_test.cpp
/**
* @file lazy single design
* @datetime 2022-06-03
* @version v0.0.1
*/
#include <iostream>
#include "lazy_single.h"
using namespace std;
/**
* [lazy_single_test description]
* @method lazy_single_test
* @author zc
* @datetime 2022-06-04
* @copyright [copyright]
* @version v0.0.1
* @return [description]
*/
lazy_single *lazy_single::m_pInstance = nullptr;
int lazy_single_test( void )
{
lazy_single *instance1 = lazy_single::getInstance();
lazy_single *instance2 = lazy_single::getInstance();
return 0;
}
2.2.1 饿汉式单例
hungry_single.h
/**
* @file hungry single design
* @datetime 2022-06-03
* @version v0.0.1
*/
#include <iostream>
using namespace std;
/**
* @class hungry_single
* @datetime 2022-06-03
* @version v0.0.1
*/
class hungry_single
{
public:
~hungry_single( )
{
cout << "This is < hungry_single > destructor!" << endl;
}
static hungry_single *getInstance( )
{
return m_pInstance;
}
private:
hungry_single( )
{
cout << "This is < hungry_single > constructor!" << endl;
}
hungry_single( hungry_single& ) = delete;
hungry_single& operator = ( const hungry_single& ) = delete;
static hungry_single *m_pInstance;
};
hungry_single_test.cpp
/**
* @file lazy single design
* @datetime 2022-06-03
* @version v0.0.1
*/
#include <iostream>
#include "hungry_single.h"
using namespace std;
/**
* [lazy_single_test description]
* @method lazy_single_test
* @author zc
* @datetime 2022-06-04
* @copyright [copyright]
* @version [v0.0.1]
* @return [description]
*/
hungry_single *hungry_single::m_pInstance = new hungry_single;
int hungry_single_test( void )
{
hungry_single *instance1 = hungry_single::getInstance();
hungry_single *instance2 = hungry_single::getInstance();
return 0;
}
2.2.1 单例类的模板
my_single.h
/**
* @file template single design
* @datetime 2022-06-03
* @version v0.0.1
*/
#include <iostream>
using namespace std;
/**
* @class template_single
* @datetime 2022-06-03
* @version v0.0.1
*/
#include <iostream>
using namespace std;
template <typename T>
class my_single {
public:
//外部获取单例的接口
static T& getInstance( )
{
static Token token;
static T instance(token); //函数静态变量可以实现延时构造。
return instance;
}
//禁止拷贝
my_single(const my_single&) = delete;
my_single& operator=(const my_single&) = delete;
protected:
//只有子类才能获取令牌。
struct Token{};
//构造和析构函数私有化
my_single() = default;
virtual ~my_single() = default;
};
my_single_test.cpp
/**
* @file my single design
* @datetime 2022-06-03
* @version v0.0.1
*/
#include <iostream>
#include "my_single.h"
using namespace std;
//具体单例模式
//
class Single_1 : public my_single<Single_1> {
public:
//Token保证,父类需要调用,其他人无法调用。
Single_1(Token token)
{
cout << "construct: " << this << endl;
//做你想做的事情。
}
~Single_1() = default;
//禁止拷贝
Single_1(const Single_1&) = delete;
Single_1& operator=(const Single_1&) = delete;
};
class Single_2 : public my_single<Single_2> {
public:
//Token保证,父类需要调用,其他人无法调用。
Single_2(Token token)
{
cout << "construct: " << this << endl;
//做你想做的事情。
}
~Single_2() = default;
//禁止拷贝
Single_2(const Single_2&) = delete;
Single_2& operator=(const Single_2&) = delete;
};
/**
* [my_single_test description]
* @method my_single_test
* @author zc
* @datetime 2022-06-04
* @version v0.0.1
* @return [description]
*/
int my_single_test( void )
{
Single_1 &s1 = Single_1::getInstance();
Single_1 &s2 = Single_1::getInstance();
Single_2 &s3 = Single_2::getInstance();
Single_2 &s4 = Single_2::getInstance();
return 0;
}
参考
C++ 单例模式详解
单例模式(C++实现)
C++单例模式模板 (简单易懂且有效)
C++ 单例模式深度剖析 一
单例模式中的懒汉模式和饿汉模式是什么?区别又是什么?
c++单例模式模板
C++之单例类模板
深入探索单例设计模式:以百度 Apollo 为例