【设计模式】单例模式,嵌套类实现释放对象内存

本文介绍了一种使用C++11实现的线程安全单例模式,通过静态成员和互斥锁确保了实例的唯一性和获取实例的效率。文章详细展示了如何避免多次实例化同一对象,节省系统资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.概述

     单例模式就是一个类只能被实例化一次 ,也就是只能有一个实例化的对象的类。减少每次都new对象的开销,节省系统资源,同时也保证了访问对象的唯一实例。常用于
如下场景:
1.频繁实例化然后销毁的对象。
2.创建对象太耗时,消耗太多资源,但又经常用到。

二.代码实现

C++11代码实现,

/********************************************************
Copyright (C),  2016-2018,
FileName:		main
Author:        	woniu201
Created:       	2018/12/05
Description:	C++ 单例模式
********************************************************/
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;


class CSingleton
{
public:

	static CSingleton* GetInstance()
	{
		if (singleton == NULL) //两个NULL可以提高获取实例效率
		{
			std:lock_guard<mutex> lock(m_mutext); //封装lock,unlock,出作用域会自动解锁
			if (singleton == NULL)
			{
				singleton = new CSingleton();
				static CGC c1;
			}
		}
		return singleton;
	}

	class CGC  //类中嵌套类,用于释放对象
	{
	public:
		~CGC()
		{
			if (CSingleton::singleton)
			{
				delete CSingleton::singleton;
				CSingleton::singleton = NULL;
				cout << "释放对象" << endl;
			}
		}
	};

	//成员函数
	void fun1()
	{
		cout << "this is test!" << endl;
	}
private:
	CSingleton() {}
	static CSingleton* singleton;
	static std::mutex m_mutext;
};

//类外初始化静态成员
CSingleton* CSingleton::singleton = NULL;
mutex CSingleton::m_mutext;

/************************************
@ Brief:		线程函数
@ Author:		woniu201 
@ Created:		2018/12/05
@ Return:            
************************************/
void threadFun()
{
	CSingleton* obj = CSingleton::GetInstance();
	obj->fun1();
	cout << "实例地址:" << obj << endl;
}

int main()
{
	thread t1(threadFun);
	thread t2(threadFun);
	t1.join();
	t2.join();
	return 1;
}

<think>好的,用户想了解如何使用智能指针结合双检锁实现单例模式。首先,我需要回忆单例模式的基本结构和双检锁的作用。单例模式确保一个类只有一个实例,并提供全局访问点。双检锁用于在多线程环境下减少同步开销,只在第一次创建实例时进行同步。 接下来,用户提到了智能指针,所以需要考虑如何用智能指针管理单例实例,避免内存泄漏。常用的智能指针有unique_ptr和shared_ptr。但单例模式通常是独占所有权的,所以unique_ptr可能更合适。不过,由于单例需要全局访问,可能需要使用shared_ptr来确保即使有多个地方持有指针,实例也能正确释放。 然后,我需要参考用户提供的引用内容。引用[4]提到单例的实现方法包括构造函数私有、防拷贝、提供静态接口返回实例。引用[2]和[3]也强调了单例的结构和实现细节。结合这些,单例类应该将构造函数私有化,并禁用拷贝构造和赋值操作。 双检锁的实现需要静态的实例指针,通常使用原始指针,但用户希望用智能指针。这里可能需要使用shared_ptr,因为unique_ptr无法被多个地方共享,而shared_ptr的引用计数可以管理生命周期。但单例模式通常希望实例一直存在,直到程序结束,所以可能需要自定义删除器,或者确保shared_ptr在程序结束时正确释放。 接下来要考虑线程安全。双检锁需要在第一次检查实例是否存在时不加锁,如果不存在,再加锁进行第二次检查,避免多次创建。使用智能指针时,需要考虑其原子操作和线程安全性。C++11后的智能指针的引用计数是原子操作的,但对象的创建仍需同步。 然后,如何将双检锁与智能指针结合。静态的实例变量需要是shared_ptr类型,并且在锁内进行初始化。使用std::call_once可能更安全,但用户明确要求双检锁,所以需要手动实现。 可能的代码结构:在getInstance方法中,先检查instance是否为空,如果为空,加锁,再次检查,然后创建实例。使用make_shared来创建实例,并赋值给静态的shared_ptr。 还需要处理析构问题,确保程序结束时实例被正确释放。可能需要定义一个静态的嵌套类,在析构函数中释放实例,或者使用自定义删除器。 最后,验证代码是否符合用户提供的引用中的要求,如构造函数私有、防拷贝、全局访问点等。同时,确保使用智能指针后,内存管理是自动的,无需手动delete。 可能的陷阱:双重检查锁定在C++中的正确实现需要考虑内存屏障,因为编译器或处理器可能会重排指令,导致未初始化的对象被访问。使用C++11的原子操作或mutex可以避免这个问题。另外,智能指针的静态局部变量可能更简洁,但用户要求双检锁,所以需要显式处理。 总结步骤:定义单例类,私有化构造和拷贝操作,静态的shared_ptr实例,双检锁机制,使用mutex保证线程安全,返回shared_ptr实例。</think>--- ### 使用智能指针结合双检锁实现单例模式C++) --- #### 一、核心实现思路 1. **单例模式核心约束** - 构造函数私有化(禁止外部构造)[^2][^4] - 禁用拷贝构造和赋值运算符[^4] - 提供全局访问接口 2. **智能指针选择** - 使用 `std::shared_ptr`:支持共享所有权(即使存在多个引用,实例仅析构一次) - 使用 `std::call_once` 或双检锁保证线程安全[^2] 3. **双检锁优化** - 减少锁竞争:仅在首次创建时加锁 - 避免指令重排:使用 `std::atomic` 或内存屏障(C++11 后原子操作默认安全) --- #### 二、完整代码实现 ```cpp #include <memory> #include <mutex> class Singleton { public: // 全局访问接口(返回 shared_ptr) static std::shared_ptr<Singleton> getInstance() { if (!instance_) { // 第一次检查(无锁) std::lock_guard<std::mutex> lock(mutex_); if (!instance_) { // 第二次检查(加锁后) instance_ = std::shared_ptr<Singleton>(new Singleton); } } return instance_; } // 禁用拷贝和赋值 Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; private: Singleton() = default; // 私有构造函数[^2] ~Singleton() = default; // 静态成员声明 static std::shared_ptr<Singleton> instance_; static std::mutex mutex_; }; // 静态成员初始化 std::shared_ptr<Singleton> Singleton::instance_ = nullptr; std::mutex Singleton::mutex_; ``` --- #### 三、关键代码解析 1. **双检锁机制** ```cpp if (!instance_) { // 第一次检查(无锁,快速路径) std::lock_guard<std::mutex> lock(mutex_); if (!instance_) { // 第二次检查(加锁后,安全路径) instance_ = std::shared_ptr<Singleton>(new Singleton); } } ``` - **性能优化**:99% 的情况下无需加锁 - **线程安全**:锁内二次检查防止重复创建[^2] 2. **智能指针管理** ```cpp std::shared_ptr<Singleton> instance_; // 自动管理生命周期 ``` - 即使多线程多次调用 `getInstance()`,共享指针保证实例唯一 - 程序退出时自动释放内存(无需手动 `delete`) 3. **防拷贝设计** ```cpp Singleton(const Singleton&) = delete; // 禁用拷贝构造[^4] Singleton& operator=(const Singleton&) = delete; // 禁用赋值运算符 ``` --- #### 四、改进与优化建议 1. **使用 `std::call_once` 替代双检锁(C++11)** ```cpp #include <mutex> class Singleton { public: static std::shared_ptr<Singleton> getInstance() { std::call_once(flag_, []() { instance_ = std::make_shared<Singleton>(); }); return instance_; } private: static std::shared_ptr<Singleton> instance_; static std::once_flag flag_; }; ``` - 更简洁且无锁竞争风险 2. **防止用户手动析构** ```cpp ~Singleton() = default; // 保持私有,防止外部 delete ``` --- #### 五、线程安全验证 | 场景 | 双检锁 | `std::call_once` | |--------------------|--------|------------------| | 首次并发访问 | 安全 | 安全 | | 后续访问 | 无锁 | 无锁 | | 指令重排风险 | 需原子操作 | 无风险 | --- #### 六、应用场景 - **日志系统**:全局唯一的日志写入器[^1] - **配置管理**:共享配置数据源 - **硬件接口**:独占访问设备驱动 --- ### 相关问题 1. 双检锁在 C++ 中是否绝对线程安全? 2. 如何用 `unique_ptr` 实现单例模式? 3. 单例模式在分布式系统中会有什么问题? --- ### 引用说明 [^1]: 单例模式通过唯一实例实现资源节约和全局访问。 : 单例模式要求构造函数私有化并禁用拷贝操作。 [^4]: 使用静态接口返回单例实例是标准实现方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码农code之路

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

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

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

打赏作者

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

抵扣说明:

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

余额充值