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 访问资源就行。

相关推荐
玖玥拾6 分钟前
C# 语言进阶(十五)C# 游戏服务端 MySQL 数据库
服务器·开发语言·网络·数据库·mysql·c#
铅笔侠_小龙虾26 分钟前
Rust 学习目录
开发语言·学习·rust
Darkwanderor35 分钟前
对Linux的进程控制的研究
linux·运维·c++
云泽8081 小时前
从零吃透 C++ 异常:抛出捕获、栈展开、异常重抛与编码规范详解
开发语言·c++·代码规范
灯澜忆梦1 小时前
GO---可见性规则
开发语言·golang
REDcker1 小时前
libdatachannel 快速入门
c++·webrtc·datachannel
逝水无殇1 小时前
C# 文件的输入与输出详解
开发语言·数据库·后端·c#
这是个栗子2 小时前
前端开发中的常用工具函数(八)
开发语言·前端·javascript
凤凰院凶涛QAQ2 小时前
《Java版数据结构 & 集合类剖析》栈与队列:“push/pop 是栈的灵魂,offer/poll 是队列的骨架——四组 API,两种人生”
java·开发语言·数据结构
研☆香3 小时前
为什么变量声明在赋值前也能使用?—— 深入理解 JavaScript 变量提升与作用域
开发语言·前端·javascript