news 2026/5/12 5:02:41

C++——智能指针 shared_ptr

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/12 5:02:05

Mixtral-8x7B本地部署:混合量化与动态专家卸载实战指南

1. 项目概述与核心思路拆解最近在折腾大语言模型本地部署的朋友&#xff0c;估计都对Mixtral-8x7B这个“庞然大物”又爱又恨。爱的是它作为开源MoE&#xff08;专家混合&#xff09;模型的标杆&#xff0c;性能直逼GPT-3.5&#xff1b;恨的是它那惊人的参数量——尽管是稀疏激活…

作者头像 李华
网站建设 2026/5/12 5:01:10

平面变压器PCB绕组设计实战:从原理到布局的工程考量

1. 平面变压器PCB绕组设计基础 第一次接触PCB平面变压器时&#xff0c;我被它紧凑的结构惊艳到了。传统绕线变压器像个臃肿的胖子&#xff0c;而PCB平面变压器则像练过瑜伽的运动员 - 所有绕组都被压扁在PCB层间&#xff0c;磁芯直接嵌入电路板。这种设计在服务器电源、车载充电…

作者头像 李华
网站建设 2026/5/12 5:00:36

从CTFHub靶场实战,聊聊JWT那些容易被忽略的安全坑(附工具和脚本)

从CTFHub靶场实战剖析JWT安全漏洞的攻防艺术 在网络安全竞赛和实际渗透测试中&#xff0c;JSON Web Token&#xff08;JWT&#xff09;的安全问题一直是高频考点。许多开发者虽然了解JWT的基本概念&#xff0c;却在实战中频频踩坑。本文将通过CTFHub靶场中的四个典型JWT漏洞场景…

作者头像 李华
网站建设 2026/5/12 5:00:26

2026耐腐蚀低压开关柜选型逻辑:技术要点与工程验证

为什么“耐腐蚀”成为低压开关柜的刚需指标在输变电设备领域&#xff0c;低压开关柜作为配电系统的末端核心环节&#xff0c;直接决定了电能分配的安全性与可靠性。然而&#xff0c;近年来随着工业制造向高附加值领域转型、新能源项目向沿海纵深推进&#xff0c;以及老旧电网设…

作者头像 李华