当前位置: 首页 > news >正文

【C++】智能指针介绍

在这里插入图片描述
🎆个人主页:夜晚中的人海

在这里插入图片描述

今日语录:当你在荒废时间,有多少人在拼命。别在最该奋斗的日子,选择了安逸。

文章目录

  • 🚀一、RAII和智能指针
  • 🎉二、C++标准库智能指针的使用
    • 删除器
  • 🚘三、智能指针的原理及其模拟实现
    • 1.auto_ptr
    • 2.unique_ptr
    • 3.重点:shared_ptr
  • 🎡四、shared_ptr和weak_ptr之间的关系
    • 1.shared_ptr循环引用的问题
    • 2.weak_ptr的介绍及其模拟实现
  • 🏝️五、C++11和boost智能指针中的关系
  • ⭐六、内存泄漏
    • 1.什么是内存泄漏以及造成的危害
    • 2.如何避免内存泄漏

🚀一、RAII和智能指针

RAII是Resource Acquisition Is Initialization的缩写,它的意思是获取资源立即初始化。本质是⼀种利用对象生命周期来管理获取到的动态资源,避免资源泄漏

智能指针类除了满足RAII的设计思路,还要方便对资源进行访问,所以智能指针类还会想迭代器类⼀样,重载 operator*/operator->/operator[]等运算符,

例:

template<class T>
class smart_ptr
{
public:smart_ptr(T* ptr):_ptr(ptr){}~smart_ptr(){cout << "delete[]" << _ptr << endl;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T& operator[](size_t i){return _ptr[i];}
private:T* _ptr;
};

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

C++标准库中的智能指针都在< memory >这个头文件下面,因此在使用前需要包含这一头文件。除了weak_ptr以外,其它都符合RAII和像指针⼀样访问的行为,原因主要是解决智能指针拷贝时的思路不同

C++标准库主要提供了四种智能指针:
auto_ptr: 它是在C++98中设计出来的智能指针,其特点是拷贝时把被拷贝对象的资源管理权转移给拷贝对象,这个设计是非常糟糕的,因为它会让被拷贝的对象悬空,当我们对其进行访问时会发生报错。因此在日常工作中不建议使用这一指针

以下这三种指针都是在C++11中设计出来的
unique_ptr:它的意思是唯一指针,其特点是不支持拷贝只支持移动,因此在不需要拷贝的场景下就可以使用它

shared_ptr: 它的意思是共享指针,其特点是支持拷贝也支持移动,因此在需要拷贝的场景下就可以使用它

weak_ptr:它的意思的弱指针,其完全不同于上面的智能指针,因为它不支持RAII,也就意味着不能用它直接管理资源。其作用是用来解决循环引用的问题

例:

struct Date
{int _year;int _month;int _day;Date(int year = 1,int month = 1,int day = 1):_year(year),_month(month),_day(day){}~Date(){cout << "~Date()" << endl;}
};int main()
{auto_ptr<Date> ap1(new Date);//ap1的管理权转移给ap2auto_ptr<Date> ap2(ap1);//报错:对空指针进行访问ap1->_year++;unique_ptr<Date> up1(new Date);//报错:不支持拷贝unique_ptr<Date> up2(up1);//支持移动,移动后up1也为空,不能对其进行访问unique_ptr<Date> up2(move(up1));shared_ptr<Date> sp1(new Date);//支持拷贝也支持移动shared_ptr<Date> sp2(sp1);shared_ptr<Date> sp3(move(sp1));return 0;
}

删除器

智能指针析构时默认是进行delete释放资源,这也就意味着如果不是new出来的资源交给智能指针管理,析构时就会崩溃。因此智能指针支持在构造时给⼀个删除器,而删除器本质就是⼀个可调用对象,这个可调用对象中实现你想要的释放资源的方式。即当我们在构造智能指针时,给了定制的删除器,智能指针析构时就会调用我们设计出的删除器去释放资源

struct Date
{int _year;int _month;int _day;Date(int year = 1,int month = 1,int day = 1):_year(year),_month(month),_day(day){}~Date(){cout << "~Date()" << endl;}
};template<class T>
class DeleteArray
{
public:void operator()(T* ptr){delete[] ptr;}
};template<class T>
void DeleteArrayFunc(T* ptr)
{deletr[] ptr;
};int main()
{//这样去构造程序会报错//unique_ptr<Date> up(new Date[10]);//shared_ptr<Date> sp(new Date[10]);//解决方案1:因为new[]经常使用,因此unique_ptr和shared_ptr //实现了⼀个特化版本,这个特化版本在析构时用的delete[] unique_ptr<Date[]> up(new Date[10]);shared_ptr<Date[]> sp(new Date[10]);//解决方案2:使用仿函数对象做删除器//这里需要注意:unique_ptr和shared_ptr在支持删除器的方式是有所不同的//unique_ptr在类模板参数支持的,shared_ptr是构造函数参数支持的 unique_ptr<Date, DeleteArray<Date>> up1(new Date[10]);shared_ptr<Date> sp1(new Date[10], DeleteArray<Date>());// 函数指针做删除器  unique_ptr<Date, void(*)(Date*)> up3(new Date[5], DeleteArrayFunc<Date>);shared_ptr<Date> sp3(new Date[5], DeleteArrayFunc<Date>);// lambda表达式做删除器 auto del = [](Date* ptr) {delete[] ptr; };unique_ptr<Date, decltype(del)> up4(new Date[10], del);shared_ptr<Date> sp4(new Date[10], del);return 0;
}

使用时需要注意以下几点:
1.shared_ptr 除了支持用指向资源的指针构造,还支持 make_shared 用初始化资源对象的值直接构造。

2.shared_ptr 和 unique_ptr 都支持了operator bool的类型转换,如果智能指针对象是⼀个空对象没有管理资源,则返回false,否则返回true,意味着我们可以直接把智能指针对象给if判断是否为空。

3.shared_ptr 和 unique_ptr 在构造函数中都得使用explicit来修饰,防止普通指针隐式类型转换成智能指针对象。

例:

int main()
{shared_ptr<Date> sp1(new Date(2025, 10, 9));shared_ptr<Date> sp2 = make_shared<Date>(2025, 10, 9);if (sp1)cout << "sp1 is not nullptr" << endl;return 0;
}

🚘三、智能指针的原理及其模拟实现

1.auto_ptr

template<class T>
class auto_ptr
{
public:auto_ptr(T* ptr):_ptr(ptr){}auto_ptr(auto_ptr<T>& sp):_ptr(sp._ptr){sp._ptr = nullptr;}auto_ptr<T>& operator=(auto_ptr<T>& ap){if (this != &ap){if (_ptr)delete _ptr;_ptr = ap._ptr;ap._ptr = nullptr;}return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private:T* _ptr;
};

2.unique_ptr

	template<class T>class unique_ptr{public://	加explicit是防止普通指针隐式类型// 	转化为智能指针对象explicit unique_ptr(T* ptr):_ptr(ptr){}~unique_ptr(){if (_ptr){cout << "delete:" << _ptr << endl;delete _ptr;}}T* operator->(){return _ptr;}T& operator*(){return *_ptr;}//不支持左值的拷贝构造unique_ptr(const unique_ptr<T>& up) = delete;//不支持左值的赋值unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;//支持右值的拷贝构造unique_ptr(unique_ptr<T>&& up):_ptr(up._ptr){up._ptr = nullptr;}//支持右值的赋值unique_ptr<T>& operator=(unique_ptr<T>&& up){delete _ptr;_ptr = up._ptr;up._ptr = nullptr;}private:T* _ptr;};
}

3.重点:shared_ptr

shared_ptr的拷贝底层是采用引用计数的方式来实现的,让多个shared_ptr对象共用同一份资源
在这里插入图片描述

	template<class T>class shared_ptr{public:explicit shared_ptr(T* ptr = nullptr):_ptr(ptr), _pcount(new int(1)){}//拷贝shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr),_pcount(sp._pcount){++(*_pcount);}~shared_ptr(){if (--(*_pcount) == 0){delete _ptr;delete _pcount;}}//赋值shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (_ptr != sp._ptr){if (--(*_pcount) == 0){delete _pcount;delete _ptr;}_ptr = sp._ptr;_pcount = sp._pcount;++(*_pcount);}return *this;}T* operator->(){return _ptr;}T& operator*(){return *_ptr;}private:T* _ptr;int* _pcount;};

定制删除器版本:

template<class T>
class shared_ptr
{
public:explicit shared_ptr(T* ptr = nullptr):_ptr(ptr),_pcount(new int(1)){}template<class D>shared_ptr(T* ptr, D del):_ptr(ptr),_del(del),_pcount(new int(1)){}void release(){if (--(*_pcount) == 0){_del(_ptr);delete _pcount;_ptr = nullptr;_pcount = nullptr;}}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr),_pcount(sp._pcount),_del(sp._del){++(*_pcount);}~shared_ptr(){release();}shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (_ptr != sp._ptr){release();_ptr = sp._ptr;_pcount = sp._pcount;++(*_pcount);_del = sp._del;}return *this;}int use_count() const{return *_pcount;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}
private:T* _ptr;int* _pcount;function<void(T*)> _del = [](T* ptr) {delete ptr; };
};

🎡四、shared_ptr和weak_ptr之间的关系

1.shared_ptr循环引用的问题

shared_ptr在大多数情况下管理资源都非常合适,但在循环引用场景下会出现内存泄漏的问题,因此我们要学会使用weak_ptr来解决这类问题
在这里插入图片描述
当n1和n2进行析构时,引用计数为1不为0,导致内存泄漏
在这里插入图片描述

2.weak_ptr的介绍及其模拟实现

weak_ptr不支持RAII,也不支持访问资源,它支持绑定到shared_ptr,绑定到shared_ptr时,不增加shared_ptr的引用计数,因此就可以解决循环引用的问题

• weak_ptr也没有重载operator*和operator->等,因为它不参与资源管理,但如果它绑定的shared_ptr已经释放了资源,我们在对其进行访问,那是非常危险的。因此它设计出expired帮助我们检查指向的资源是否过期,使用use_count也可获取shared_ptr中的引用计数

例:
在这里插入图片描述

🏝️五、C++11和boost智能指针中的关系

Boost库是为C++语言标准库提供扩展的⼀些C++程序库的总称,C++11及之后的新语法和库有很多都是从Boost中来的

C++98中产生了第⼀个智能指针auto_ptr

C++boost库也给出了更实用的scoped_ptr/scoped_array和shared_ptr/shared_array和weak_ptr等

C++TR1,引入了shared_ptr等,不过注意的是TR1并不是标准版

C++11,引入了unique_ptr和shared_ptr和weak_ptr。需要注意的是unique_ptr对应boost的scoped_ptr。并且这些智能指针的实现原理是参考boost中的实现的

⭐六、内存泄漏

1.什么是内存泄漏以及造成的危害

内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存,⼀般是忘记释放或者发生异常释放程序未能执行导致的。

危害:普通程序在资源少的情况下运行⼀会就结束了出现内存泄漏问题也不是很大。但在长期运行的程序以及有大量资源的情况下出现内存泄漏,影响是非常大的,如操作系统、后台服务、长时间运行的客户端等等,不断出现内存泄漏会导致可用内存不断变少,各种功能响应越来越慢,最终卡死。

2.如何避免内存泄漏

1.养成良好的编码规范,申请的内存空间记着匹配的去释放

2.尽量使用智能指针来管理资源,如果在某些场景比较特殊的情况下,采用RAII思想自己造个轮子来管理

3.定期使用内存泄露工具

今天的分享就到这里了,感谢您的评阅,我们下期再见!

http://www.dtcms.com/a/465583.html

相关文章:

  • 利用腾讯混元大模型搭建Cherry Studio自有知识库,打造“智能第二大脑”
  • 咸阳做网站的公司有哪些电商卖货平台有哪些
  • 浏阳网站建设卷云网络做网页的it网站
  • 广东省省考备考(第一百二十天10.10)——资料分析、判断推理(强化训练)
  • 常用的C++压测框架
  • 强化学习之父 Richard Sutton :大模型是死路一条
  • 【YOLO 模型入门】(1)一文读懂 YOLO:从核心概念到检测原理
  • redis消息队列
  • AI任务相关解决方案21-一种基于大语言模型、多智能体协作平台MCP、Agent、RAG技术的项目投标智能化系统与方法
  • 做一个能注册用户的网站深圳优化网站公司
  • 哈尔滨seo建站怎么用wordpress建立自己的网站
  • Frigate - IP 摄像头开源程序实时目标检测 NVR
  • Ubuntu系统使用指南
  • BERT文本分类超参数优化实战:从13小时到83秒的性能飞跃
  • 传输层协议之TCP协议
  • k8s中实现pod热加载
  • 医疗网站建设中心百度做个网站要多少钱
  • 万盛网站建设公司视频网站制作
  • H618-内核驱动的第一个hello world
  • IEEE内期刊论文爬取
  • 网站设计创意wordpress实现图片幻灯展示效果
  • docker部署安装milvus(向量数据库)、配置依赖etcd和MinIO
  • 网站建设属开票核定税种江苏徐州工程交易网
  • 鸿蒙开发6--HarmonyOS标签页应用开发实战教程
  • 使用ts-jest进行TypeScript的单元测试配置
  • 108、23种设计模式之模板方法模式(17/23)
  • React 19.2.0: 新特性与优化深度解析
  • {【MySQL】深入解析InnoDB存储引擎的MVCC机制与实现原理}
  • 吉安哪家做网站的公司好html简单网站成品免费
  • 以太网PHY芯片全面解析:物理层核心、单多口设计与实战选型