《探索C++11:现代语法的内存管理优化“性能指针”(下篇)》

前引:在传统C++开发中,手动内存管理常伴随资源泄漏、悬垂指针等隐患,成为系统稳定性的致命威胁。C++11引入的智能指针体系通过所有权语义自动生命周期管理 ,从根本上重构了内存安全范式。本文将深入剖析unique_ptrshared_ptrweak_ptr三大核心组件的设计哲学,结合资源所有权转移模型$$ \mathcal{R}(ownership) \rightarrow \mathcal{R}_{smart}(destruction) $$揭示其如何通过编译期契约替代运行时风险,为现代C++工程注入强健性与优雅性!

目录

【一】智能指针定义与用途

(1)定义

(2)用途

【二】智能指针:auto_ptr

【三】智能指针:unique_ptr

【四】智能指针:share_ptr

【五】智能指针:weak_ptr

【六】定制删除器

(1)何为定制删除器

(2)为何使用定制删除器

(3)删除器使用

(4)实现定制删除器


【一】智能指针定义与用途

(1)定义

C++11更新出来的智能指针简而言之就是帮我们解决内存忘记泄漏的问题,它本质上是一个类模板,借助类模板的Delete自动析构函数完成内存的释放。智能指针通过 RAII 机制(weak_ptr除外)管理资源,默认使用delete释放内存!

(2)用途

智能指针主要解决以下几个问题:

(1)忘记手动释放内存

(2)多次释放同一块内存

根据需求的不同,智能指针也有多个,下面一一介绍使用和特性,以及如何手撕!

【二】智能指针:auto_ptr

这个智能指针的设计很不实用,且给我们带来了一定的分担,总体上来说增加了工程Bug,但我们还是要了解一下!
**特性:**赋值的时候转移资源,很符合移动语义,但这里不是临时对象(将亡值)
例如:有一个A用auto_ptr初始化,当A拷贝给B时,A的资源就给B了,那A就变成空指针了

实现:

cpp 复制代码
template<class T>
class auto_ptr
{
public:
	auto_ptr(T* ptr)
		:_ptr(ptr)
	{ }

	auto_ptr(auto_ptr<T>& date)
	{
		_ptr = date._ptr;
		date._ptr = nullptr;
	}

	~auto_ptr()
	{
		delete _ptr;
		_ptr = nullptr;
	}

	T* operator->()
	{
		return _ptr;
	}

	T& operator*()
	{
		return *_ptr;
	}

private:
	T* _ptr = nullptr;
};

【三】智能指针:unique_ptr

**特性:**unique单词有唯一的意思,所以这个智能指针会独占资源,即不允许转移资源

实现:

cpp 复制代码
class unique_ptr
{
public:
	unique_ptr(T* ptr)
		:_ptr(ptr)
	{ }

	//不让拷贝
	unique_ptr(const unique_ptr& date) = delete;

	~unique_ptr()
	{
		delete _ptr;
		_ptr = nullptr;
	}

	//不让赋值
	void operator=(const unique_ptr& date) = delete;

	T* operator->()
	{
		return _ptr;
	}

	T& operator*()
	{
		return *_ptr;
	}

private:
	T* _ptr = nullptr;
};

【四】智能指针:share_ptr

**特性:**share单词有分享的意思,在这里就是同一类型共同拥有一块资源,例如:

实现:

精髓:无论是空间资源指针还是计数空间指针,都采用指向同一块资源的形式

注意:重点是下面的赋值和delete

(1)既然共同占用一块资源,那么在Delete时,应该当是最后一个对象Delete时再释放资源,否 则对同一块资源多次Delete会崩溃

(2)为了确保是最后一个资源我们需要一个计数器:这里采用的是指针,如果是普通整型,那么 当有多个同类对象时无法准确计数,采用静态变量也没有指针形式快捷简单,例如:

cpp 复制代码
template<class T>
class share_ptr
{
public:
	share_ptr(T* ptr)
		:_ptr(ptr)
		,_count(new int(1))
	{ }

	//拷贝构造
	share_ptr(share_ptr<T>& date)
	{
		//共同占用资源
		_ptr = date._ptr;
		_count = date._count;
		(*_count)++;
	}

	//Delete释放
	~share_ptr()
	{
		//如果是最后一个对象说明可以释放资源了,否则减减_count即可
		if (*_count == 1)
		{
			delete _ptr;
			_ptr = nullptr;

			delete _count;
			_count = nullptr;
		}
		else
		{
			(*_count)--;
		}
	}

	//赋值重载
	void operator=(share_ptr<T>& date)
	{
		//如果是自己赋值给自己
		if (_ptr == date._ptr)
		{
			return;
		}
		//开始共占资源
		_ptr = date._ptr;
		*_count++;
		_count = date._count;
	}

	T* operator->()
	{
		return _ptr;
	}

	T& operator*()
	{
		return *_ptr;
	}

private:
	T* _ptr = nullptr;
	int* _count = nullptr;
};

效果:

可以看到同类型的share_ptr是共同占用一块资源的,而且delete是根据*_count释放的

【五】智能指针:weak_ptr

**特性:**weak单词有虚弱的意思,它的"虚"体现在只对资源可以访问,不参与引用计数,专门解决 share_ptr带来的循环引用问题,例如:

不支持RAII(资源的生命周期与对象的生命周期绑定),不支持单独管理资源

循环引用问题:

循环引用就是share_ptr在初始化前后互相指向的问题(类似:你抓着我的头发,我抱住你的腿,双方都说:"你先放我就放开"),逻辑:

  1. 初始状态a 指向 A 对象(A 的引用计数为 1),b 指向 B 对象(B 的引用计数为 1)。
  2. 互相引用后
    • a->b_ptr = bB 对象的引用计数从 1 变为 2(ba->b_ptr 共同引用)。
    • b->a_ptr = aA 对象的引用计数从 1 变为 2(ab->a_ptr 共同引用)。
  3. main 函数结束时
    • a 离开作用域,A 对象的引用计数从 2 减为 1(剩余 b->a_ptr 引用)。
    • b 离开作用域,B 对象的引用计数从 2 减为 1(剩余 a->b_ptr 引用)。
  4. 最终状态AB 的引用计数都停留在 1(互相引用),永远不会变为 0,因此它们的析构函数不会被调用,内存永远不会释放。

实现:

只是可以访问share_ptr的资源,不参与计数

cpp 复制代码
template<class T>
class weak_ptr
{
public:
	
	//拷贝构造
	weak_ptr(share_ptr<T>& date)
	{
		//共同占用资源
		_ptr = date.Get();
	}

	//赋值重载
	void operator=(share_ptr<T>& date)
	{
		//共同占用资源
		_ptr = date.Get();
	}

	T* operator->()
	{
		return _ptr;
	}

	T& operator*()
	{
		return *_ptr;
	}

private:
	T* _ptr = nullptr;
};

【六】定制删除器

(1)何为定制删除器

在 C++11 中,大部分智能指针通过 RAII 机制管理资源,默认使用delete释放内存。但实际开发中,资源类型远不止动态内存(如文件句柄、网络连接、数组、互斥锁等),这些资源需要特殊的释放逻辑(如fcloseclosedelete[]等)。定制删除器(CustomDeleter) 就是为解决这一问题而生的:它允许我们为智能指针指定自定义的资源释放逻辑,让智能指针能够管理任意类型的资源!(简而言之:处理除动态内存之外的释放资源问题)
注:删除器是一个可调用对象 (函数指针、lambda、std::function等)

(2)为何使用定制删除器

动态数组需要定制删除器的核心原因是:

(1)动态数组通过 new[] 分配,必须用 delete[] 释放(与 new/delete 不兼容)

(2)智能指针默认使用 delete 释放资源,与数组的释放要求冲突

(3)定制删除器可以显式指定 delete[],确保分配与释放逻辑匹配,避免内存泄漏或未定义行为

(3)删除器使用

传仿函数:

cpp 复制代码
template<class T>
struct Function
{
	void operator()(T* ptr)
	{
		delete[] ptr;
		ptr = nullptr;
	}
};
cpp 复制代码
shared_ptr<string> V1(new string[10],Function<string>());

传Lambda:

cpp 复制代码
shared_ptr<string> V2(new string[10], [](string* ptr) { delete[] ptr; ptr = nullptr; });

文件释放:

cpp 复制代码
shared_ptr<FILE> V3(fopen("text.cpp", "c"), [](FILE* ptr) {fclose(ptr); });
(4)实现定制删除器
cpp 复制代码
template<class T>
class share_ptr
{
public:
	share_ptr(T* ptr)
		:_ptr(ptr)
		,_count(new int(1))
	{ }
   
    //定制删除器
	template<class D>
	share_ptr(T* ptr,D del)
		:_ptr(ptr)
		,_count(new int(1))
		,_del(del)
	{}

	//拷贝构造
	share_ptr(share_ptr<T>& date)
	{
		//共同占用资源
		_ptr = date._ptr;
		_count = date._count;
		(*_count)++;
	}

	//Delete释放
	~share_ptr()
	{
		//如果是最后一个对象说明可以释放资源了,否则减减_count即可
		if (*_count == 1)
		{
			_del = _ptr;

			delete _count;
			_count = nullptr;
		}
		else
		{
			(*_count)--;
		}
	}

	//赋值重载
	void operator=(share_ptr<T>& date)
	{
		//如果是自己赋值给自己
		if (_ptr == date._ptr)
		{
			return;
		}
		//开始共占资源
		_ptr = date._ptr;
		*_count++;
		_count = date._count;
	}

	T* operator->()
	{
		return _ptr;
	}

	T& operator*()
	{
		return *_ptr;
	}

	T* Get()
	{
		return _ptr;
	}

private:
	T* _ptr = nullptr;
	int* _count = nullptr;
	function<void(T*)> _del = [](T* ptr) {delete ptr; };
};
相关推荐
charlie1145141914 小时前
前端三件套简单学习:HTML篇1
开发语言·前端·学习·html
kebeiovo4 小时前
项目必备流程图,类图,E-R图实例速通
开发语言·r语言·流程图
软件开发-NETKF88884 小时前
JSP到Tomcat特详细教程
java·开发语言·tomcat·jsp·项目运行
小欣加油5 小时前
leetcode 912 排序数组(归并排序)
数据结构·c++·算法·leetcode·排序算法
星竹晨L5 小时前
【C++】类和对象(三)
c++
ftswsfb5 小时前
现代C++:现代C++?
开发语言·c++
乌萨奇也要立志学C++5 小时前
【C++详解】C++ 智能指针:使用场景、实现原理与内存泄漏防治
开发语言·c++
minji...5 小时前
C++ 详细讲解vector类
开发语言·c++
颖川守一5 小时前
c6-类和对象-对象特征-类对象做对象成员
c++