【C++】单例模式

一、简介

1. 含义

单例模式是常见的设计模式之一,本质是:只提供唯一的类的实例,其具有全局变量的特点,在任意位置都可以使用该类提供的接口访问到其唯一的实例对象。

2. 特点

  • 全局唯一实例:static特性
  • 不允许用户声明并定义实例,构造函数权限为private
  • 禁止赋值和拷贝
  • 用户使用接口获取实例:使用静态类成员函数
  • 线程安全

二、单例模式实现

1. 饿汉式

该单例模式是指单例实例在类装载时就构建,并被立即执行初始化。

#include <iostream>

class HungrySingleton
{
private:
	static HungrySingleton* instance;
	HungrySingleton()
	{
		std::cout << "构造函数!" ;
	}
	~HungrySingleton()
	{
		std::cout << "析构函数!" ;
	}
public:
	static HungrySingleton* getInstance()
	{
		return instance;
	};
};

HungrySingleton* HungrySingleton::instance = new HungrySingleton();

int main()
{
	HungrySingleton* test = HungrySingleton::getInstance();
	return 0;
}
  • 优点:
    • 线程安全
    • 在类加载时创建静态对象,调用时反应速度快
  • 缺点:
    • 资源利用率不高,getInstance()可能永远都不会执行
    • 如果类是多态的, 那静态成员变量初始化顺序无法保证

2. 懒汉式

该单例模式直到使用时才会实例化对象,即调用geInstance() 方法时才会创建一个单例对象。

#include <iostream>

class LazySingleton 
{
private:
	static LazySingleton* m_InstancePtr;

private:
	LazySingleton()
	{
		std::cout << "构造函数执行!"; 
	}
	LazySingleton(LazySingleton&) = delete;
	LazySingleton& operator=(const LazySingleton&) = delete;

public:
	~LazySingleton() 
	{
		std::cout << "析构函数执行!";
	}

	static LazySingleton* getInstance() 
	{
		if (m_InstancePtr == nullptr) 
		{
			m_InstancePtr = new LazySingleton();
		}

		return m_InstancePtr;
	}
};
//静态成员:类内声明,类外初始化
LazySingleton* LazySingleton::m_InstancePtr = nullptr;

int main()
{
	LazySingleton* test = LazySingleton::getInstance();
	return 0;
}
  • 优点: 不调用getInstance()就不会占用内存。
  • 缺点: 懒汉模式在单线程中没问题,但多个线程同时访问时就可能创建多个实例,而且这些实例不是同一个对象,虽然后创建的实例会覆盖先创建的实例,但仍会存在拿到不同对象的情况。若想解决这个问题则需要加锁

3. 局部静态变量

该单例模式通过局部静态变量的特性保证了线程安全 , 不需要使用共享指针、加锁等操作,代码更加简洁。

#include <iostream>

class MagicStaticSingleton
{
private:
	MagicStaticSingleton()
	{
		std::cout << "构造函数执行!" << std::endl;
	}

public:
	~MagicStaticSingleton()
	{
		std::cout << "析构函数执行!" << std::endl;
	}

	MagicStaticSingleton(const MagicStaticSingleton&) = delete;
	MagicStaticSingleton& operator=(const MagicStaticSingleton&) = delete;

	static MagicStaticSingleton& getInstance()
	{
		static MagicStaticSingleton instance;
		return instance;
	}
};

int main()
{
	MagicStaticSingleton& test = MagicStaticSingleton::getInstance();
	return 0;
}
  • 优点: 当变量在初始化时,并发同时进入声明语句,并发线程将会阻塞等待初始化结束。这样保证了并发线程在获取静态局部变量的时候一定是初始化过的,所以具有线程安全性。

4. 模板类实现

子类需要将自己作为模板参数T传递给SingletonT模板,并且需要将父类声明为友元,这样才能调用子类的私有构造函数

#include <iostream>

template<typename T>
class SingletonT
{
public:
	static T& getInstance()
	{
		static T instance;
		return instance;
	}

	virtual ~SingletonT()
	{
		std::cout << "析构函数!" << std::endl;
	}

	SingletonT(const SingletonT&) = delete;
	SingletonT& operator =(const SingletonT&) = delete;

protected:
	SingletonT()
	{
		std::cout << "构造函数!" << std::endl;
	}
};

class TestSingleton : public SingletonT<TestSingleton>
{
	friend class SingletonT<TestSingleton>;

public:
	TestSingleton(const TestSingleton&) = delete;
	TestSingleton& operator =(const TestSingleton&) = delete;

private:
	TestSingleton() = default;
};

int main()
{
	TestSingleton& test = TestSingleton::getInstance();
	return 0;
}

三、总结

推荐使用局部静态变量这种单例实现方式,不建议使用懒汉式,若需要单例复用则建议使用模板类实现


如果这篇文章对你有所帮助,渴望获得你的一个点赞!

在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OpenC++

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值