智能指针详解

article/2025/9/19 19:46:15

目录

前言

1、为什么需要智能指针?

2、智能指针的原理

3、智能指针的分类

3.1 auto_ptr

3.2 unique_ptr

3.3 shared_ptr


前言

C++11中引入了智能指针的特性,本文将详细介绍智能指针的使用。


1、为什么需要智能指针?

我们来看一段代码:

void Func()
{int* p1 = new int;int* p2 = new int;cout << div() << endl;delete p1;delete p2;
}

在这段代码中,如果在div中抛异常,很显然会造成内存泄漏。为了避免内存泄漏,我们希望有一种指针可以做到自动管理内存释放。 

2、智能指针的原理

RALL:RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:

// 使用RAII思想设计的SmartPtr类
template<class T>
class SmartPtr {
public:SmartPtr(T* ptr = nullptr): _ptr(ptr){}~SmartPtr(){if(_ptr)delete _ptr;}T& operator*() {return *_ptr;}
T* operator->() {return _ptr;}private:T* _ptr;
};

 智能指针的原理:

  •  RAII特性
  • 重载operator*和opertaor->,具有像指针一样的行为。

3、智能指针的分类

在c++的库中定义了几种不同的智能指针,头文件为memory。在智能指针进行拷贝构造或者赋值时,会出现两个指针指向同一块内存的情况,这样在释放空间时就会出现问题。针对这个问题有三种不同的解决方法,对应c++库中的三种指针,前两种都是不成熟的做法,简单了解即可。

3.1 auto_ptr

C++98版本的库中就提供了auto_ptr的智能指针。

auto_ptr的实现原理:管理权转移的思想,简单的将原指针设为空,把管理权交给新指针。

auto_ptr(auto_ptr<T>& sp)
:_ptr(sp._ptr)
{// 管理权转移sp._ptr = nullptr;
}

但是这样会出现很大的问题,因为有时候指针的权限已经发生了转移,但是使用指针的人并不知道,很可能造成越界访问。

结论:auto_ptr是一个失败设计,很多公司明确要求不能使用auto_ptr。

3.2 unique_ptr

unique_ptr的实现原理:简单粗暴的防拷贝。

unique_ptr(const unique_ptr<T>& sp) = delete;
unique_ptr<T>& operator=(const unique_ptr<T>& sp) = delete;

这个指针时是不能拷贝的,所以在很多情况下不能使用。

3.3 shared_ptr

C++11中开始提供更靠谱的并且支持拷贝的shared_ptr。shared_ptr的原理是通过引用计数的方来实现多个shared_ptr对象之间共享资源。

  • shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
  • 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。
  • 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;
  •  如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。

注意:

  • 引用计数支持多个拷贝管理同一个资源,最后一个析构对象释放资源。为了保证指向同一块资源的shared_ptr具有相同的count,count要开辟在堆上,每个shared_ptr对象中有一个int类型指针,指向相同资源的shared_ptr中的int类型指针指向相同的count。这里不能为了达到共用效果定义静态类型count,因为静态类型是给所有对象共用,指向不同资源的指针也会共用一个count。
  • 由于引用计数count是共用的,在多线程中可能会发生安全问题,所以要加锁来保护。

简单模拟实现如下:

template<class T>class shared_ptr{public:shared_ptr(T* ptr):_ptr(ptr), _pcount(new int(1)), _pmutex(new mutex){}~shared_ptr(){RealseRef();}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount), _pmutex(sp._pmutex){AddRef();}shared_ptr<T>& operator= (const shared_ptr<T>&sp){if (_ptr == sp._ptr){return *this;}//先释放掉现在指向的资源RealseRef();_ptr = sp._ptr;_pmutex = sp._pmutex;_pcount = sp._pcount;AddRef();return *this;}private:void AddRef(){_pmutex->lock();(*_pcount)++;_pmutex->unlock();}void RealseRef(){_pmutex->lock();bool flag = false;if (--(*_pcount) == 0){delete _ptr;delete _pcount;flag = true;}_pmutex->unlock();if (flag == true){delete _pmutex;}}T* _ptr;int* _pcount;mutex* _pmutex;};

 shared_ptr的循环引用:

在某些情况下会使用到循环引用,这时候可能会出现问题。

struct ListNode
{int _data;shared_ptr<ListNode> _prev;shared_ptr<ListNode> _next;~ListNode(){ cout << "~ListNode()" << endl; }
};int main()
{shared_ptr<ListNode> node1(new ListNode);shared_ptr<ListNode> node2(new ListNode);node1->_next = node2;node2->_prev = node1;return 0;
}

智能指针指向的空间中也保存有智能指针,且两个智能指针指向的空间中保存的智能指针还指向对方的空间,这称为循环引用。

  • 第一步node1和node2指向AB两块空间,两块空间的引用计数都为1。
  • 第二步A空间的_next指向B空间,B空间的_prev指向A空间,引用计数变成2。
  • 第三步node1和node2析构,调用析构函数,但是并没有成功释放空间。原因是node1和node2析构后,两块空间的引用计数都减为1,因为这两块空间的_next和_prev还没有析构,A空间的_next在空间内部,想要析构A空间的_next必须先释放A空间,想要释放A空间必须让引用计数等于0,也就是必须先析构B空间的_prev,同理析构B空间的_prev的前提是释放B空间,那就要求B空间的引用计数为0,前提是析构A空间的_next。这就变成了一个鸡生蛋和蛋生鸡的问题,发生卡死。

为了解决这个问题。C++中增加了weak_ptr。在引用计数的场景下,把节点中的_prev和_next改成weak_ptr就可以了。

struct ListNode
{int _data;weak_ptr<ListNode> _prev;weak_ptr<ListNode> _next;~ListNode(){ cout << "~ListNode()" << endl; }
};

 weak_ptr只是单纯的赋值,不会使引用计数++,析构后也不负责释放空间。 weak_ptr就好比一个局外人,只起到索引的作用。

删除器:

析构智能指针时需要的操作并不相同,有时候传给智能指针的指针可能会一下new出来了多个值,这时候就需要delete[ ],或者传过去的指针是打开文件的指针,析构时不应该delete而是进行关闭文件操作。shared_ptr设计了一个删除器来解决这个问题。

del是一个可调用对象,通过传入del可以自定义析构智能指针时进行的操作。

template<class T>struct DelArr{void operator()(const T* ptr){cout << "delete[]:"<<ptr<<endl;delete[] ptr;}};
void test_shared_ptr_deletor(){std::shared_ptr<ListNode> spArr(new ListNode[10], DelArr<ListNode>());std::shared_ptr<FILE> spfl(fopen("test.txt", "w"), [](FILE* ptr){cout << "fclose:" << ptr << endl;fclose(ptr); });}

 总结

本文主要简单介绍了智能指针的使用,希望能给大家带来帮助。江湖路远,来日方长,我们下次见~


http://chatgpt.dhexx.cn/article/XTMfXuH5.shtml

相关文章

【c++复习笔记】——智能指针详细解析(智能指针的使用,原理分析)

&#x1f482; 个人主页:努力学习的少年&#x1f91f; 版权: 本文由【努力学习的少年】原创、在CSDN首发、需要转载请联系博主&#x1f4ac; 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 目录 一. 智能指针的基本概念 二. 智能指针的定义和使用 三. a…

C++ 智能指针 - 全部用法详解

为什么要学习智能指针&#xff1f; 咳咳&#xff0c;这个问题不是问大家的&#xff0c;是询问我自己的&#xff01; 我依稀记得刚离校出来找实习工作那会儿(2020年7月)&#xff0c;去面试一份工作&#xff0c;面试官问了我许多问题&#xff0c;其中有一个问题就是问&#xff1a…

AndroidStudio编译时 CXX1405 错误解决

AndroidStudio 编译带C的native库项目时报错&#xff0c; [CXX1405] error when building with cmake using D:\WorkSpace\AndroidXXXX\app\src\main\cpp\CMakeLists.txt: Build command failed.解决方法&#xff1a; cmd 到 Android SDK的cmake路径下&#xff0c;执行 cmake …

硬件速攻-AT24CXX存储器

AT24C02是什么&#xff1f; AT24CXX是存储芯片&#xff0c;驱动方式为IIC协议 实物图&#xff1f; 引脚介绍&#xff1f; A0 地址设置角 可连接高电平或低电平 A1 地址设置角 可连接高电平或低电平 A2 地址设置角 可连接高电平或低电平 1010是设备前四位固定地址 &#xf…

【dbus-cxx】libsigc++ 和 dbus-cxx 在 Ubuntu 中的编译和配置

文章目录 参考资料配置环境cmakelibsigcdbus-cxx 例程server.cppclient.cppMakefile运行结果 此帖作为自己学习记录使用&#xff0c;尚未使用交叉编译&#xff0c;仅在本地使用 参考资料 需先了解DBUS基础知识&#xff0c;建议看DBUS官方文档 DBUS-CXX也是建议看官方文档&…

正点原子IIC例程讲解笔记(三)——24cxx.c中函数理解

目录 一、24C02 简介 二、在 AT24CXX 指定地址写入一个数据&#xff1a; 三、在ATC24XX指定地址读出一个数据 四、检查AT24CXX是否正常&#xff1a;u8 AT24CXX_Check(void) 五、在 AT24CXX 里面的指定地址开始写入长度为 Len 的数据 六、在 AT24CXX 里面的指定地址开始读出…

windows用VS2019下编译log4cxx日志库

一、下载相关库文件 获取log4cxx源码包&#xff1a;http://logging.apache.org/log4cxx/index.html 获取依赖库apr和apr-util源码包:http://archive.apache.org/dist/apr/apr-1.2.11-win32-src.zip http://archive.apache.org/dist/apr/apr-util-1.2.10-win32-src.zip 编译apr…

log4cxx编译

本人进行过win7 64位操作系统和win10家庭版的log4cxx编译&#xff0c;使用的是vs2015&#xff0c;下面是详情。 1.sed下载 sed-4.2.1-bin.zip、sed-4.2.1-dep.zip下载地址&#xff1a;http://gnuwin32.sourceforge.net/packages/sed.htm 下载后&#xff0c;将sed的两个压缩包解…

【RT-Thread Master】at24cxx软件包使用笔记

硬件介绍 RT-Thread版本&#xff1a;V4.1.0软件包名称&#xff1a;at24cxxMCU型号&#xff1a;AT32F407VET7EEPROM型号&#xff1a;AT24C16 使用说明 1、使用menuconfig将软件包添加进入工程&#xff0c;路径如下所示。 2、把IIC总线打开&#xff0c;这里使用软件IIC&#…

linux下编译和安装log4cxx,ubuntu下log4cxx安装使用

需要安装log4cxx&#xff0c;安装的过程中可是充满了坎坷。。。最大的问题是在make log4cxx时&#xff0c;总是报undefined XML什么什么的错误&#xff0c;查了一下也没解决了&#xff0c;然后把apr-utils删了重新装了一下就好了。。 log4cxx现在是apache的一个项目&#xff0c…

linux下编译和安装log4cxx,RedHat如何安装log4cxx日志库

log4cxx日志库是一种动态库&#xff0c;用于记录c的日志&#xff0c;那么RedHat系统下要如何安装log4cxx日志库呢&#xff1f;下面小编就给大家介绍下RedHat安装log4cxx日志库的步骤&#xff0c;感兴趣的朋友不妨来了解下吧。 首先&#xff0c;我得到信息&#xff0c;安装这个库…

AT24Cxx读写全面理解

AT24Cxx - 电可擦可写E2PROM 芯片介绍 基础介绍\引脚介绍 AT24Cxx系列EEPROM是由美国Mcrochip公司出品&#xff0c;1-512K位的支持I2C总线数据传送协议的串行CMOS E2PROM&#xff0c;可用电擦除&#xff0c;可编程自定时写周期&#xff08;包括自动擦除时间不超过10ms&#…

mongodb-cxx-driver使用

mongocxx driver 是构建在 MongoDB C driver 之上的 1.首先需要安装mongo-c-driver wget https://github.com/mongodb/mongo-c-driver/releases/download/ 1.23.1/mongo-c-driver-1.23.1.tar.gz tar xzf mongo-c-driver-1.23.1.tar.gz cd mongo-c-driver-1.23.1 mkdir cmak…

老胡的周刊(第095期)

老胡的信息周刊[1]&#xff0c;记录这周我看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。 &#x1f3af; 项目 tabby[2] 自托管的 AI 编码助手&#xff0c;…

程序员养生指北

吴小胖第八次推送 阅读时间预计3分钟~ 熬夜篇 互联网人熬夜是不能避免的&#xff0c;原因却各不相同。 不加班的时候&#xff0c;总会对自己说&#xff0c;今天一定早睡&#xff0c;然鹅... 午休篇 熬夜的程序员总想依靠午休补觉&#xff0c;然鹅... 更不幸的是&#xff0c;互联…

老杨说运维 | 中国IT运维市场的现状与趋势

文章内容来源《第一新声》 对擎创科技CEO杨辰(老杨)的专访 前言&#xff1a; 中国目前正面临百年未有之大变局&#xff0c;在这个变局中&#xff0c;不稳定性和不确定性正在增强。疫情持续反复、国际形势变化多端&#xff0c;导致国内多个行业出现发展增速下降、产供销节奏打…

老杨说运维 | 非常重要,事关转型

《荀子》有云&#xff1a;“水能载舟&#xff0c;亦能覆舟。”在公司日常运营过程中&#xff0c;数据指标就像是水&#xff0c;孕育着生命&#xff0c;承载着万物。科学的数据指标能指引公司在正确的道路上不断前进&#xff0c;使平淡无常的业务焕发新生&#xff0c;而不合理的…

学习springcloud的一些心得体会——老卫的天气预报系统

1&#xff1a;建立天气预报springboot系统 首先先建立一个天气预报的springboot系统&#xff0c;具体流程如下&#xff1a; &#xff08;1&#xff09;从cityList.xml中获取城市信息&#xff0c; &#xff08;2&#xff09;然后根据下面的链接获取各个城市的天气预报信息&am…

学习springboot项目的一些心得-----老卫的博客系统

去年年底接触了springboot框架&#xff0c;这两天复习了一遍&#xff0c;主要是跟着老卫博客系统这个课程学习的。 springboot介绍&#xff1a;总的感觉springboot就是基于spring开发的一套框架&#xff0c;好处就是不用配置复杂的依赖包&#xff0c;xml的一些文件&#xff0c…

thymeleaf和spring boot的集成踩坑总结(练习项目是老卫的博客项目)

bug1: 2019-08-08 16:44:38.102 ERROR 19810 — [nio-8080-exec-8] org.thymeleaf.TemplateEngine : [THYMELEAF][http-nio-8080-exec-8] Exception processing template “users/list”: Error resolving template “users/list”, template might not exist or might not be …