C++ - 智能指针
1.1 RAll
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
- 不需要显式地释放资源。
- 采用这种方式,对象所需的资源在其生命期内始终保持有效。
在C++中,智能指针一共定义了4种: auto_ptr、unique_ptr、shared_ptr 和 weak_ptr。其中 auto_ptr 在 C++11已被摒弃,在C++17中已经移除不可用。
1.2 智能指针概念
在c++中,动态内存的管理式通过一对运算符来完成的:new,在动态内存中为对象分配空间并返回一个指向该对象的指针,我们可以选择对对象进行初始化;
delete,接受一个动态对象的指针,销毁该对象,并释放与之关联的内存。动态内存的使用很容易出现问题,因为确保在正确的时间释放内存是极其困难的。有时使用完对象后,忘记释放内存,造成内存泄漏的问题。
- 所谓的智能指针本质就是一个类模板,它可以创建任意的类型的指针对象,当智能指针对象使用完后,对象就会自动调用析构函数去释放该指针所指向的空间。
下面是智能指针的基本框架,所有的智能指针类模板中都需要包含一个指针对象,构造函数和析构函数。
2. 原始指针的问题
原始指针的问题大家都懂,就是如果忘记删除,或者删除的情况没有考虑清楚,容易造成悬挂指针(dangling pointer)或者说野指针(wild pointer)。
我们看个简单的例子
objtype *p = new objtype();
p -> func();
delete p;
上面的代码结构是我们经常看到的。里面的问题主要有以下两点:
1.代码的最后,忘记执行delete p的操作。
2.第一点其实还好,比较容易发现也比较容易解决。比较麻烦的是,如果func()中有异常,delete p语句执行不到,这就很难办。有的同学说可以在func中进行删除操作,理论上是可以这么做,实际操作起来,会非常麻烦也非常复杂。
此时,智能指针就可以方便我们控制指针对象的生命周期。在智能指针中,一个对象什么情况下被析构或被删除,是由指针本身决定的,并不需要用户进行手动管理,是不是瞬间觉得幸福感提升了一大截,有点幸福来得太突然的意思,终于不用我自己手动删除指针了。
3. 常见的智能指针
3.1 auto_ptr
auto_ptr是c++98版本库中提供的智能指针,该指针解决上诉的问题采取的措施是管理权转移的思想,也就是原对象拷贝给新对象的时候,原对象就会被设置为nullptr,此时就只有新对象指向一块资源空间。
如果auto_ptr调用拷贝构造函数或者赋值重载函数后,如果再去使用原来的对象的话,那么整个程序就会崩溃掉(因为原来的对象被设置为nullptr),这对程序是有很大的伤害的.所以很多公司会禁用auto_ptr智能指针。
3.1.1 特点
①只能有一个智能指针占用对象的所有权。
3.1.2 案例一
3.1.3 案例二
注: auto_ptr不能赋值和用于初始化另一个对象。如果进行了此类操作,则原智能指针对象无效。
3.1.4 案例三
3.1.5 案例四
3.1.6 缺点
①auto_ptr不能赋值和用于初始化另一个对象,如果经行了此类操作,则原智能指针对象无效。——例如 案例二和案例三。
②auto_ptr只能管理单个对象,不能管理对象数组。——例如 案例四以表明。auto_ptr析构函数使用的是delete,而不是delete[ ]。
③在C++11标准中已经废弃auto_ptr。——原因:<1>它可能导致对同一块堆空间进行多次delete。<2>当两个智能指针都指向同一个堆空间时,每个智能指针都会delete一下这个堆空间, 这会导致未定义行为。
3.2 unique_ptr
unique_ptr是c++11版本库中提供的智能指针,它直接将拷贝构造函数和赋值重载函数给禁用掉,因此,不让其进行拷贝和赋值。
unique_ptr的拷贝函数和赋值重载函数
3.2.1 特点
①unique_ptr可以看成是auto_ptr的代替品,因为它对对象的所有权比较专一,所以叫unique
②不允许进行拷贝构造和赋值操作。
③允许函数返回unique_ptr类型指针
④支持对象数组。
3.2.2 案例一
3.2.3 案例二:对案例一进行正确修改

3.2.4 案例三
对应特点③——允许函数返回unique_ptr类型指针
3.2.5 unique_ptr的缺点
无法进行拷贝构造和赋值操作。
3.3 sharshared_ptr
share_ptr是c++11版本库中的智能指针,shared_ptr允许多个智能指针可以指向同一块资源,并且能够保证共享的资源只会被释放一次,因此是程序不会崩溃掉。
3.3.1 特点
①shared_ptr定义智能指针A拷贝构造产生智能指针B的时候,A和B共享一个对象。
②shared_ptr定义智能指针A赋值给shared_ptr定义的智能指针B的时候,A和B共享一个对象。
3.3.2 实现原理
shared_ptr采用的是引用计数原理来实现多个shared_ptr对象之间共享资源:
- shared_ptr在内部会维护着一份引用计数,用来记录该份资源被几个对象共享。
- 当一个shared_ptr对象被销毁时(调用析构函数),析构函数内就会将该计数减1。
- 如果引用计数减为0后,则表示自己是最后一个使用该资源的shared_ptr对象,必须释放资源。
- 如果引用计数不是0,就说明自己还有其他对象在使用,则不能释放该资源,否则其他对象就成为野指针。
引用计数是用来记录资源对象中有多少个指针指向该资源对象。
销毁过程:
3.3.3 案例一 ——创建对象
3.3.4 案例二 —— 拷贝构造、赋值
3.3.5 案例三 —— 两个shared_ptr同时指向普通指针
缺点:循环引用的时候,会带来内存泄漏。
p指针指向空间被释放了两次,这种问题被比喻成“二龙治水”,部分编译器对此做了优化不会报错。
shared_ptr智能指针详解图
3.3.6 案例四 —— 循环引用问题
可以看到目前的引用计数都是2,当代码执行完之后,才会调用析构函数,可以看到这两个都没有调用析构函数,产生这种问题的原因是:A引用了B,B又引用了A。
解决这种问题需要使用弱引用计数 weak_ptr
改完之后再重新编译一下
可以看到引用计数都是1,当sp1和sp2脱离生命周期的时候就会执行析构函数。
3.4 weak_ptr
弱引用指针,不会影响对象的引用计数,通常与shared_ptr
一起使用。
3.4.1 特点
①weak_ptr是为了配合shared_ptr而引入的一种智能指针来协助shared_ptr工作。
②weak_ptr的含义为“弱引用”,它的构造和析构不会引起引用计数的增加或减少。
③它可以从一个shared_ptr或另一个weak_ptr产生。
④他没有重载*和 -> ,所以不能通过它访问对象内部的成员。
⑤可以使用它提供的lock()获得一个可用的shared_ptr对象。
3.4.2 案例一:weak_ptr智能指针创建

3.4.3 案例二
调用lock()创建shared_ptr指针时才会引用实际对象。
在lock()成功时会延长shared_ptr对象的生命周期,因为它递增了一个引用计数。
expired()函数用作判断weak_ptr智能指针是否过期,过期返回true。
expired()用于判断object2是否过期,即object2中是否还有指向内容,如果没有过期就继续。
3.4.4 案例三 帮助shared_ptr解决循环引用问题
4. 智能指针总结
-
std::unique_ptr
:独占型指针,保证同一时间内只有一个智能指针可以指向对象。 -
std::shared_ptr
:共享型指针,当存在多个shared_ptr
指向同一个对象时,该对象不会自动销毁。 -
std::weak_ptr
:弱引用指针,不会影响对象的引用计数,通常与shared_ptr
一起使用。 -
std::auto_ptr
(已废弃):自动释放型指针,在C++11后被std::unique_ptr
取代。
更多参考文档:
————————————————
- std::shared_ptr:共享的智能指针。 链接:https://subingwen.cn/cpp/shared_ptr/
- std::unique_ptr:独占的智能指针。 链接:https://subingwen.cn/cpp/unique_ptr/
- std::weak_ptr:弱引用的智能指针。 链接:https://subingwen.cn/cpp/weak_ptr/
————————————————
版权声明:本文为CSDN博主「L2018026029」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/L2018026029/article/details/126571301
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)