C++ 智能指针

什么是智能指针

智能指针是自动帮你管理内存、防止内存泄漏的包装类,是现代 C++ 用来代替裸指针的工具,解决了裸指针在某些地方的痛点。比如:

1.忘记 delete ,导致内存泄漏。

2.提前 delete ,后面还要继续用,导致悬空指针,野指针。

3.异常抛出时,函数提前退出,delete 永远执行不到。

RAII

要了解智能指针,我们先从它的设计思路开始。智能指针的设计思路是 RAII (Resource Acquisition Is Initialization),这是一种 "资源获取即初始化" 的编程思想,是所有智能指针的底层原理,也是现代 C++ 管理资源的核心范式。

RAII 的核心逻辑是:把资源的生命周期,和一个栈对象的生命周期绑定在一起 。RAII 在获取资源时把资源委托给一个对象,接着控制对资源的访问,资源在对象生命周期内始终保持有效,最后在对象析构的时候释放资源,这样保障了资源的正常释放,避免资源泄漏问题。

C++标准库智能指针的使用

使用智能指针需要包含头文件 <memory>。

C++98:

智能指针在 C++98 就已经出现,但效果不尽人意。

auto_ptr:auto_ptr是 C++98 就出现的智能指针,但它的设计十分糟糕,属于 C++ 的一次失败的尝试。它的逻辑是:拷贝时,把拷贝对象的资源的管理权转移给拷贝对象,旧的对象会被置空。但这种置空是不会显示表现出来的,让人以为旧指针能用,如果此时访问就会报错。

C++11:

之前 C++98 搞出来的智能指针太糟糕,很多公司明令禁止使用。C++ 委员会也知道只是一个非常差劲的设计,于是在 C++11 移动语义出现后,迅速抛弃了 auto_ptr,搞出了几个新的智能指针。

unique_ptr: unique_ptr 不支持拷贝,只支持移动。unique_ptr 也会把旧的指针置空,但它的移动是显示的,也就是说你在使用它时你应该自己清楚旧指针会被置空,如果你还去访问旧指针就是你自己的问题了。在不需要拷贝的场景建议使用它。

shared_ptr :shared_ptr 支持拷贝,也支持移动。它的底层是使用引用计数的方式实现的。shared_ptr 除了支持用指向资源的指针进行构造,还支持用 make_shared 用初始化资源对象的值直接构造。

weak_ptr: weak_ptr 完全不同于以上指针,它不支持 RAII ,这也意味着不能用它直接管理资源。weak_ptr 的产生是为了解决循环引用的问题,这个后面细讲。

智能指针析构时默认进行 delete 释放资源,所以如果不是 new 出来的资源,交给智能指针管理会崩溃。智能指针支持在构造时给一个删除器,在它调用析构时会调用删除器去释放资源,这解决了不同 delete 的需求。

循环引用

循环引用问题出现在 shared_ptr 上,它会导致资源没有得到释放,内存泄漏,想要解决这个问题,需要用到 weak_ptr 。

先看例子:现在有 n1 和 n2 两个 shared_ptr:

假如 n1 的资源中又有一个shared_ptr 指针 _next 指向 n2,n2 的资源中又有一个shared_ptr 指针 _prev 指向 n1。注意此时计数器的变化:

当我们析构 n1 和 n2 时, 按理说资源应该被释放,但是 share_ptr 的资源释放是看的计数器释放为空,所以此时资源不会被释放:

这很显然并不符合实际需求,会导致内存泄漏。

weak_ptr 可以很好地解决这个问题。前面说过:weak_ptr 不支持 RAII 。它不支持绑定资源,只支持绑定 shared_ptr ,所以它不会增加 shared_ptr 的引用计数,我们将上面的 _next 和 _prev 指针设置为 weak_ptr 指针,就可以解决循环引用问题。

("来源:cplusplus.com")

weak_ptr 没有重载 operator* 和 operator-> 等,因为它不支持绑定资源,所以用它去访问资源是危险的,但它支持 expired 检查指向的资源是否过期,use_count 也可以获取 shared_ptr 的引用计数。weak_ptr 想访问资源时,可以调用 lock 返回一个管理资源的 shared_ptr ,如果资源已经被释放,返回的 shared_ptr 是一个空指针,如果没有释放,则通过返回的 shared_ptr 访问资源就行。

相关推荐
凉茶钱几秒前
【C++】string 常用函数详解
开发语言·c++·string
Wang's Blog11 分钟前
Java框架 SpringCloud 快速入门: 从服务拆分到注册中心的落地路线
java·开发语言·spring cloud
可乐鸡翅yeah_27 分钟前
hls.js 缓冲区参数 maxBufferLength、maxBufferSize 通俗讲解与业务调参
开发语言·javascript·ios·音视频·safari·m3u8
Wang's Blog29 分钟前
Java框架 SpringCloud 快速入门: 全局过滤器(GlobalFilter)自定义鉴权
java·开发语言·spring cloud
白山编程大哥32 分钟前
C语言篇:可变参数函数
c语言·开发语言
(Charon)34 分钟前
【C++面试】程序崩溃如何定位:从Segmentation fault到GDB、Core Dump与ASan
开发语言·c++
汉克老师38 分钟前
GESP2026年9月认证C++七级( 第三部分编程题(1、必经之路))精讲
c++·gesp·小学生·学c++编程
“AI国潮设计-小江”42 分钟前
《Python+SDXL实战:用ControlNet精准控制“英歌舞戚风蛋糕”质感,附批量生成脚本》
开发语言·人工智能·python·prompt·aigc
1024奇点1 小时前
【仓颉语言入门 · 第26课】
开发语言·ide·开源
jayhgq1 小时前
新一代Python包与项目管理工具——UV
开发语言·python·uv