C++ 可继承单例模版的两种实现

一、什么是单例

单例 Singleton 是设计模式的一种,其特点是只提供唯一一个类的实例,具有全局变量的特点,在任何位置都可以通过接口获取到那个唯一实例;
具体运用场景如:

  • 设备管理器,系统中可能有多个设备,但是只有一个设备管理器,用于管理设备驱动;
  • 数据池,用来缓存数据的数据结构,数据在全局中只需要一份,单例可避免数据结构多次创建浪费内存;
  • 工具类,对一些常用函数进行封装的工具类,在全局中也只需要一份;
  • 其他在全局中唯一的对象。

二、单例分类

单例模式可以分为懒汉式(饱汉式)和饿汉式,两者之间的区别在于创建实例的时间点不同:

  • 懒汉式(饱汉式):延迟加载,指系统运行中,实例并不存在,只有当需要使用该实例时,才会去创建并使用实例。(这种方式要考虑线程安全)

    • 优点:天生线程安全,无需关注线程安全问题。且相比饿汉式第一次调用时效率更高。
    • 缺点:在不需要此对象的时候对象已经加载,在某个时间段内占用了不必要的内存。
    • 空间换时间
  • 饿汉式:立即加载,指系统一运行,就初始化创建实例,当需要时,直接调用即可。(本身就线程安全,没有多线程的问题)

    • 优点:在未第一次用到此对象时,对象并不占用空间,节省一定时间段内的内存。
    • 缺点:在对象创建时,需要关注线程安全问题。且第一次调用会加载目标对象,耗时需关注。
    • 时间换空间

总结:其实你想用啥模式用啥模式,人家单例主打的一个单一实例,重点也不在什么时候加载上,把析构、构造、拷贝构造干掉,保持全局唯一对象即可。况且,你那个对象,十有八九程序刚跑起来就用上了,个人感觉大多情况下两种模式差距很小。

三、单例实现要点

  • 构造函数和析构函数为private类型,目的禁止外部构造和析构
  • 拷贝构造和赋值构造函数为private类型,目的是禁止外部拷贝和赋值,确保实例的唯一性
  • 类里有个获取实例的静态函数,可以全局访问

四、C++单例可继承模版具体实现(懒汉模式)

实现代码

#ifndef __SINGLE_HPP__
#define __SINGLE_HPP__

#include 
#include 
using std::shared_ptr;

//懒汉式(延迟加载)单例模版
//智能指针无法托管私有构造函数的类(继承类的问题),故自制RAII
template<typename T>
class Single
{
public:
	template<typename... Args>
	static T* getInstence(Args&&... args)
	{
		//双检锁,避免每次调用都加锁
		if (nullptr == _pInstance)
		{
			std::lock_guard<std::mutex> lock(_mutex);
			if (nullptr == _pInstance)
				_pInstance = new T(std::forward<Args>(args)...);
		}
		return _pInstance;
	}

//基类构造函数为protected,保证子类可以继承
protected:
	//用于自动销毁 RAII
	class AutoRelease
	{
	public:
		AutoRelease() = default;
		~AutoRelease()
		{
			if (nullptr != _pInstance)
				delete _pInstance;
		}
	};

	Single() = default;
	virtual ~Single() = default;
	Single(const Single&) = delete;
	Single& operator=(const Single&) = delete;
	Single(const Single&&) = delete;
	Single& operator=(const Single&&) = delete;

private:
	static T* _pInstance;
	static AutoRelease _autoRelease;
	static std::mutex _mutex;
};

template<typename T>
T* Single<T>::_pInstance = nullptr;

template<typename T>
typename Single<T>::AutoRelease Single<T>::_autoRelease;

template<typename T>
std::mutex Single<T>::_mutex;

//静态内部变量实现方式
template<typename T>
class Singleton {
public:
	template<typename... Args>
	static T& getInstence(Args&&... args) noexcept(std::is_nothrow_constructible<T>::value) {
		static T _pInstance(std::forward<Args>(args)...);
		return _pInstance;
	}

protected:

	Singleton() = default;
	virtual ~Singleton() noexcept = default;
	Singleton(const Singleton&) = delete;
	Singleton& operator =(const Singleton&) = delete;
	Singleton(const Singleton&&) = delete;
	Singleton& operator =(const Singleton&&) = delete;
};
#endif // !__SINGLE_HPP__

调用实例

//RAII+静态指针变量形式
class MyClass1 : public Single<MyClass1>
{
	//父类需为子类友元,以便能够调用子类私有构造、析构函数
    friend class Single<MyClass1>;
public:
	void test()
	{
		printf("this is test function\n");
	}

private:
	MyClass1() = default;
	MyClass1(int a) {printf("MyClass1(int a), a = %d\n",a);};
	~MyClass1() = default;
};

//静态内部变量形式
class MyClass2 : public Singleton<MyClass2>
{
	friend class Singleton<MyClass2>;
public:
	void test()
	{
		printf("this is test function\n");
	}

private:
	MyClass2() = default;
	MyClass2(int a) {printf("MyClass2(int a), a = %d\n",a);};
	~MyClass2() = default;
};

int main()
{
	MyClass1* cls1 = MyClass1::getInstence(3);
	MyClass2& cls2 = MyClass2::getInstence(4);

	cls1->test();
	cls2.test();
	return 0;
}
文章摘要
  1. C++ 单例模式和可继承的单例基类模板
  2. C++实现线程安全的单例模式
  3. C++线程安全单例总结

你可能感兴趣的:(c++,单例模式,java,学习,设计模式)