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

C++——智能指针 shared_ptr

C++11中开始提供更靠谱的并且支持拷贝的shared_ptr
RAII + 具有指针类似的行为 + 引用计数

目录

一、shared_ptr介绍

二、shared_ptr的使用​编辑

三、shared_ptr的模拟实现

版本一:使用一个int类型内置成员变量,再进行拷贝构造的时候进行一次++ 之后赋值给新对象。

版本二:考虑到使用static静态成员变量可以为对象之间所共享

版本三:不使用静态成员变量,借助指针类型变量

版本四:为了线程安全,引入锁机制,在所有计数操作时加锁,保证操作原子性,加入DFDef保证析构的重载

四、涉及自定义类型智能指针变量线程安全实例


一、shared_ptr介绍

shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源
1. shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共
2.对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减
一。
3.如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源
4.如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对
象就成野指针了。

二、shared_ptr的使用

#include<iostream> using namespace std; void TestSharedPtr() { shared_ptr<int> up1(new int(10)); shared_ptr<int> up2(up1); shared_ptr<int> up3; up3 = up1; } int main() { TestSharedPtr(); _CrtDumpMemoryLeaks(); return 0; }

三、shared_ptr的模拟实现

资源可以共享:浅拷贝的基础上,可以保证资源无论被多少对象共享,最终只会释放一次。

版本一:使用一个int类型内置成员变量,再进行拷贝构造的时候进行一次++ 之后赋值给新对象。

问题: 计数有问题,不同对象之间无法实现同步共享一个计数变量!

下图就是在进行up2析构之后发现up1和up2的计数变量_count是不同值。

namespace wei { template<class T> class shared_ptr { shared_ptr(T* ptr = nullptr) : _ptr(ptr) , _count(0) { if (_ptr) { _count = 1; } } ~shared_ptr() { if (_ptr && 0 == --_count) { delete _ptr; _ptr = nullptr; } } shared_ptr(shared_ptr<T>& sp) : _ptr(sp._ptr) , _count(++sp._count) {} private: T* _ptr; int _count; }; }
版本二:考虑到使用static静态成员变量可以为对象之间所共享

析构 up2之前:

析构up2之后:

问题:当另外新的对象来的时候就会产生问题,这里的up3将所有对象所共享的_count重新经历了一次初始化,也因为所有对象共享一个_count导致了问题。

我们希望每一类对象都有属于他们的_count,up1和拷贝构造出来的up2可以使用一个_count,而新声明出来的up3对象又去使用他的_count,我们不希望他们之间进行冲突。

namespace wei { template<class T> class shared_ptr { public: shared_ptr(T* ptr = nullptr) : _ptr(ptr) { if (_ptr) { _count = 1; } } ~shared_ptr() { if (_ptr && 0 == --_count) { delete _ptr; _ptr = nullptr; } } shared_ptr(shared_ptr<T>& sp) : _ptr(sp._ptr) { _count++; } private: T* _ptr; static int _count; // 静态成员变量 }; template<class T> int shared_ptr<T>::_count = 0;// 类内声明,类外定义 }
版本三:不使用静态成员变量,借助指针类型变量

参考我们不同对象在创建时都有不同的地址空间,例如up1和up2中的_ptr就使用同一份地址,而up3中的_ptr又有它自己新的地址,能不能利用类似于_ptr的一个东西来设计一个_count存储每一堆对象们的计数。

在成员变量中新加一个指针来进行计数。

private: T* _ptr; int* _pcount;

效果如下图:

namespace wei { template<class T> class shared_ptr { public: shared_ptr(T* ptr = nullptr) : _ptr(ptr) , _pcount(nullptr) { if (_ptr) { _pcount = new int(1); } } ~shared_ptr() { if (_ptr && 0 == --(*_pcount)) { delete _ptr; delete _pcount; _ptr = nullptr; _pcount = nullptr; } } shared_ptr(shared_ptr<T>& sp) : _ptr(sp._ptr) , _pcount(sp._pcount) { (*_pcount)++; } private: T* _ptr; int* _pcount; };
版本四:为了线程安全,引入锁机制,在所有计数操作时加锁,保证操作原子性,加入DFDef保证析构的重载
#include <mutex> namespace bite { template<class T> class DFDef { public: void operator()(T*& ptr) { if (ptr) { delete ptr; ptr = nullptr; } } }; template<class T, class DF = DFDef<T>> class shared_ptr { public: // RAII shared_ptr(T* ptr = nullptr) : _ptr(ptr) , _pcount(nullptr) , _pMutex(nullptr) { if (_ptr) { _pcount = new int(1); _pMutex = new mutex(); } } ~shared_ptr() { Release(); } // 具有指针类似行为 T& operator*() { return *_ptr; } T* operator->() { return _ptr; } // 解决浅拷贝:引用计数 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 (this != &sp) { // *this要和sp去共享资源 // 1. 先让*this和离开之前的资源 Release(); // 2. *this 和 sp共享资源和计数 _ptr = sp._ptr; _pcount = sp._pcount; _pMutex = sp._pMutex; AddRef(); } return *this; } T* get() { return _ptr; } private: void AddRef() { _pMutex->lock(); ++(*_pcount); _pMutex->unlock(); } void Release() { bool flag = false; _pMutex->lock(); if (_ptr && 0 == --(*_pcount)) { DF()(_ptr); delete _pcount; _pcount = nullptr; flag = true; } _pMutex->unlock(); if (flag) { delete _pMutex; _pMutex = nullptr; } } private: T* _ptr; int* _pcount; mutex* _pMutex; }; }

四、涉及自定义类型智能指针变量线程安全实例

#include <thread> #include <mutex> struct Date { int _year = 0; int _month = 0; int _day = 0; }; void SharePtrFunc(std::shared_ptr<Date>& sp, size_t n, mutex& mtx) { cout << sp.get() << endl; for (size_t i = 0; i < n; ++i) { std::shared_ptr<Date> copy(sp); // unique_lock<mutex> lk(mtx); 保证该区域元素修改原子性 copy->_year++; copy->_month++; copy->_day++; } } void TestSharedPtr3() { std::shared_ptr<Date> sp(new Date); cout << sp.get() << endl; size_t n = 10000; mutex m; thread t1(SharePtrFunc, std::ref(sp), n, std::ref(m)); ref的用法保证传递引用 thread t2(SharePtrFunc, std::ref(sp), n, std::ref(m)); t1.join(); 进行线程等待 t2.join(); cout << sp->_year << endl; cout << sp->_month << endl; cout << sp->_day << endl; } int main() { TestSharedPtr3(); _CrtDumpMemoryLeaks(); return 0; }
http://www.jsqmd.com/news/800227/

相关文章:

  • 从匿名浏览到客户身份,SAP Internet User 的创建、编辑与权限边界
  • 终极图标资源指南:如何快速找到数千个免费图标 [特殊字符]
  • 并购获批复/注册时靴子落地:为什么慧博云通收购获批之日,就是估值修复启动之时
  • 【信息科学与工程学】【安全领域】第二十七篇 几何学在网络安全的应用(1)
  • ARM SCTLR寄存器详解:系统控制与配置实践
  • RedwoodJS协调器:终极分布式协调与一致性解决方案指南
  • a16n:实现AI编程助手配置可移植性的插件化转换工具
  • 教授你的模型从自身学习
  • Redis集群高可用:从主从复制到Cluster模式生产实战
  • EdgeDB数组操作完全指南:高效处理多维数据集合的10个技巧
  • 树莓派Wi-Fi配置全攻略:从图形界面到命令行实战
  • ARM GIC-500中断控制器调试架构与实战技巧
  • 2026热镀锌钢格板优选厂家推荐:技术过硬的不锈钢钢格板、压焊钢格板源头厂家 - 栗子测评
  • 继电器驱动器节能模式原理与应用实践
  • 调试与热重载:ASP.NET Core的完美结合
  • 从零到一:手把手教你用Python模拟金属-半导体接触的能带弯曲(附代码)
  • SPT-AKI存档编辑器:终极逃离塔科夫单机版存档修改指南
  • 图像去雾数据集总汇
  • 从TI Z-Stack到你的单片机:OSAL调度器核心源码精讲与移植避坑指南
  • 五年旅程的四个收获
  • 设计模式-工厂模式
  • 超节点大单交付公告时连续中标背后的“隐性护城河”:宝德的运营商生意为什么越做越稳
  • AR/VR立体深度计算优化:SteROI-D系统解析
  • GrandNode社区与支持:如何参与开源项目并获得帮助的完整指南
  • FMCP:多通道串口调试与自动化工具实战指南
  • 从‘破解失败’到‘成功弹窗’:复盘一次CrackMe逆向中的常见思维误区与调试技巧
  • Nacos服务发现与配置中心:微服务注册中心实战
  • C++——智能指针 weak_ptr
  • 终极指南:3大微服务性能测试工具对比(JMeter vs Gatling vs k6)
  • 从‘古董’工具Cain看网络安全演进:当年的ARP欺骗与密码嗅探,今天还管用吗?