【c++】【智能指针】shared_ptr底层实现
【c++】【智能指针】shared_ptr底层实现
智能指针之前已经写过了,但是考虑到不够深入,应该再分篇写写。
1 shared_ptr
1.1 shared_ptr 是什么
std::shared_ptr是一个类模板,它的对象行为像指针,但是它还能记录有多少个对象共享它管理的内存对象。
多个std::shared_ptr可以共享同一个对象。
当最后一个std::shared_ptr被销毁时,它会自动释放它所指向的对象。
1.2 shared_ptr创建和销毁
可以通过make_shared<>函数来创建。
可以通过拷贝或赋值另一个shared_ptr来创建。
eg:sp1和sp2指向同一个对象,内存对象的引用计数为2。当sp1被销毁时,引用计数减为1,sp2仍然指向该对象。当sp2被销毁时,引用计数减为0,内存对象被销毁。
1.3 shared_ptr的底层原理
element_type* _M_ptr; // 所管理对象的地址.
__shared_count<_Lp> _M_refcount; // 引用计数块地址.
std::shared_ptr在内部其只有两个指针成员:
- 一个指针是所管理的数据的地址
- 一个指针是控制块的地址
包括引用计数
weak_ptr计数
删除器(Deleter)
分配器(Allocator)
因为不同shared_ptr指针需要共享相同的内存对象,因此引用计数的存储是在 堆
上的。
而unique_ptr只有一个指针成员,指向所管理的数据的地址。因此一个shared_ptr对象的大小是它大小的两
倍。
eg:vs2022-64下:
int main()
{std::cout << sizeof(std::shared_ptr<int>) << std::endl; // 8std::cout << sizeof(std::unique_ptr<int>) << std::endl; // 4
}
1.4 std::shared_ptr的简单实现
我们通过下面这个简单的类来模拟std::shared_ptr<>的实现,来理解引用计数的实现原理。
这里我们为了简单,只实现了
- shared_ptr的拷贝构造函数、析构函数和赋值运算符函数,引用计数只是简单地用了一个int类型的内存空间
- 省略了weak_ptr的计数、删除器和分配器,不考虑多线程的情况。
- 当我们销毁一个shared_ptr时,引用计数减1。当引用计数减为0时,我们删除指向实际数据的指针和指向引用计数的指针。
- 当我们拷贝一个shared_ptr时,引用计数加1。
- 当我们赋值一个shared_ptr时,我们首先递减左侧运算对象的引用计数。如果引用计数变为0,我们就释放左侧运算对象分配的内存以及引用计数的内存。然后拷贝右侧运算对象的数据指针和引用计数指针,最后递增引用计数。
template<typename T>
class shared_ptr {
public:// 构造函数 // 初始化智能指针,传入一个裸指针(默认为 nullptr) shared_ptr(T* ptr = nullptr) : m_ptr(ptr), m_refCount(new int(1)) {}// 拷贝构造函数 // 通过另一个 shared_ptr 构造,增加引用计数 shared_ptr(const shared_ptr& other) : m_ptr(other.m_ptr), m_refCount(other.m_refCount) {// 增加引用计数 (*m_refCount)++;}// 析构函数 ~shared_ptr() {// 减少引用计数 (*m_refCount)--;// 如果引用计数为 0,释放内存 if (*m_refCount == 0) {delete m_ptr;delete m_refCount;}}// 重载赋值运算符 shared_ptr& operator=(const shared_ptr& other) {// 检查自我赋值 if (this != &other) {// 减少旧对象的引用计数 (*m_refCount)--;// 如果引用计数为 0,释放内存 if (*m_refCount == 0) {delete m_ptr;delete m_refCount;}// 复制新对象的数据和引用计数指针,并增加引用计数 m_ptr = other.m_ptr;m_refCount = other.m_refCount;// 增加引用计数 (*m_refCount)++;}return *this;}private:T* m_ptr; // 指向实际数据的指针 int* m_refCount; // 引用计数
};
ps:多线程时 引用计数的++操作需要是原子性的。考虑使用std::atomic
- 是 C++11 引入的模板类,位于头文件 中,主要用于在多线程环境下实现原子操作,从而避免数据竞争(data race),保证线程安全。
1.5 什么时候用 std::shared_ptr<T>
?
std::shared_ptr<T>
主要用于以下场景:
1. 资源创建昂贵、比较耗时的场景
- 创建对象代价很高(例如文件、网络、数据库等),不希望频繁创建和销毁。
- 通过共享指针来管理对象生命周期,避免频繁创建和释放导致的性能损耗。
示例:管理数据库连接
#include <iostream>
#include <memory>class Database {
public:Database() { std::cout << "Connecting to Database\n"; }~Database() { std::cout << "Closing Database Connection\n"; }
};void useDatabase(std::shared_ptr<Database> db) {std::cout << "Using Database\n";
}int main() {auto db = std::make_shared<Database>(); // 资源创建昂贵,使用共享指针管理useDatabase(db); // 共享所有权
}
在这个例子中,Database连接创建和释放都很昂贵,使用 shared_ptr 可以在多个对象间安全地共享连接(并非多线程),等所有使用者都释放之后才会关闭连接。
- std::shared_ptr` 的引用计数是线程安全的(即对引用计数的增加和减少是原子操作,不会导致竞争条件)
- 但对实际管理的对象的操作是非线程安全的。
ps: 之前的文章有提到
在示例中,以下部分是线程安全的:
- 引用计数的增加和减少
- 判断对象是否需要释放
- 只读访问是线程安全的
需要加锁的部分:
- 对实际对象(
Database
)的数据修改需要加锁。
如果多个线程同时修改Database
对象的内容,可能会发生数据竞争,导致未定义行为。因此,需要在访问或修改对象时加锁。
2. 需要共享资源的所有权
- 一个对象的生命周期可能同时涉及多个对象,但不清楚谁会最终释放这个对象。
std::shared_ptr
使用引用计数,确保在最后一个shared_ptr
离开作用域时才释放资源。
示例:对象被多个对象共享
#include <iostream>
#include <memory>class A {
public:A() { std::cout << "A constructed\n"; }~A() { std::cout << "A destroyed\n"; }
};int main() {std::shared_ptr<A> sp1 = std::make_shared<A>();std::shared_ptr<A> sp2 = sp1; // 引用计数 +1std::shared_ptr<A> sp3 = sp2; // 引用计数 +1std::cout << "Use count: " << sp1.use_count() << "\n"; // 输出 3sp2.reset(); // 引用计数 -1std::cout << "Use count after sp2 reset: " << sp1.use_count() << "\n"; // 输出 2sp1.reset(); // 引用计数 -1std::cout << "Use count after sp1 reset: " << sp3.use_count() << "\n"; // 输出 1sp3.reset(); // 最终释放对象
}
sp1
,sp2
,sp3
共享对A
对象的所有权。- 只有最后一个
shared_ptr
释放时,才会析构A
对象。
不适用 shared_ptr的情况
❌ 1. 存在明显的所有者 → 使用 unique_ptr
更合适。
❌ 2. 不需要共享所有权 → 使用裸指针或 unique_ptr
。
❌ 3. 循环引用问题 → 使用 weak_ptr
解决。
部分转自:https://zhuanlan.zhihu.com/p/672745555?utm_source=chatgpt.com
相关文章:
【c++】【智能指针】shared_ptr底层实现
【c】【智能指针】shared_ptr底层实现 智能指针之前已经写过了,但是考虑到不够深入,应该再分篇写写。 1 shared_ptr 1.1 shared_ptr 是什么 std::shared_ptr是一个类模板,它的对象行为像指针,但是它还能记录有多少个对象共享它…...
python拉取大视频导入deepseek大模型解决方案
使用Python拉取大视频并导入大模型,需要综合考虑数据获取、存储、处理和资源管理,确保高效稳定地处理大视频数据,同时充分利用大模型的性能,以下是分步方案及代码示例: --- 1. 分块下载大视频(避免内存溢出…...
【Python】面向对象
编程的两大特点 面向过程:着重于做什么面向对象( oop):着重于谁去做 python是面向对象语言,面向对象三大特点:封装、继承、多态 面向对象:便于代码管理,方便迭代更新。 新式类、经…...
leetcode日记(100)填充每个节点的下一个右侧节点指针
和层序遍历差不多的思路,将节点储存在队列里,一边取出节点一边放入取出节点的左右节点,直到队列空。 /* // Definition for a Node. class Node { public:int val;Node* left;Node* right;Node* next;Node() : val(0), left(NULL), right(NU…...
docker入门篇
使用docker可以很快部署相同的环境,这也是最快的环境构建,接下来就主要对docker中的基础内容进行讲解.Docker 是一个用于开发、交付和运行应用程序的开源平台,它可以让开发者将应用程序及其依赖打包到一个容器中,然后在任何环境中运行这个容器࿰…...
python语法
1. 前面先写import导入模块,完整的语法是: [from 模块名] import [模块 | 类 | 变量 | 函数 | *] [as 别名] 语法还可以是: from 模块名 import 功能名 如果import整个模块的话,需要用.功能名(),来用这个功能ÿ…...
Dify使用部署与应用实践
最近在研究AI Agent,发现大家都在用Dify,但Dify部署起来总是面临各种问题,而且我在部署和应用测试过程中也都遇到了,因此记录如下,供大家参考。Dify总体来说比较灵活,扩展性比较强,适合基于它做…...
微信小程序接入DeepSeek模型(火山方舟),并在视图中流式输出
引言: DeepSeek,作为一款先进的自然语言处理模型,以其强大的文本理解和生成能力著称。它能够处理复杂的文本信息,进行深度推理,并快速给出准确的回应。DeepSeek模型支持流式处理,这意味着它可以边计算边输…...
前端性能优化指标及优化方案
前端性能优化的核心目标是 提高页面加载速度、降低交互延迟、减少资源占用。常见的 Web 性能指标包括 LCP、FID、CLS、TTFB、TTI、FCP 等。 关键性能指标(Web Vitals) 指标优化方案 (1)LCP(Largest Contentful Paint&…...
正则化介绍
简单介绍 正则化是用于控制模型的复杂度,防止模型在训练数据上过度拟合(overfitting)。正则化通过在模型的损失函数中引入额外的惩罚项,来对模型的参数进行约束,从而降低模型的复杂度。这个额外的惩罚通常与模型参数的…...
AI时代:数字媒体的无限可能
人工智能和数字媒体技术正深刻改变着我们的生活。通过大数据分析、机器学习等技术,人工智能不仅能精准预测用户需求,还能在医疗、金融等多个领域提供高效解决方案。与此同时,数字媒体技术的进步使得信息传播更加迅速和广泛。社会计算作为新兴…...
自动化爬虫drissionpage
自动化爬虫drissionpage官网 自动化测试框架:DrissionPage DrissionPage调用工具汇总 网络爬虫工具比较-DrissionPage、Selenium、Playwright...
禁毒知识竞赛主持稿串词
尊敬的各位领导、各位来宾、参赛选手们:大家好! 在市禁毒委员会的领导下,今年我市开展了以“参与禁毒战争,构建和谐社会”为主题的禁毒宣传月活动。为了进一步加强我市禁毒宣传力度,促进社会治安的好转和社会主义物质文…...
【JDK17】Jlink一秒生成精简的JRE
之前介绍了 Java17模块化的JDK,模块化后按需使用Jlink 用于精简生成 JRE 环境,这让快速的开发环境增强了编码的愉悦感。在实际生产环境中使用 mave 进行项目的构建,其次再是精简 JRE 缩小容器镜像体积,为实现一体化的流程…...
机器学习周报--文献阅读
文章目录 摘要Abstract 1 文章内容1.1 模型结构1.1.1 LSTMAT的结构设置1.1.2 AWPSO算法优化模型 1.2 实验与结果讨论1.2.1 处理缺失数据1.2.2 模型评估指标1.2.3 比较实验1.2.4 消融实验(ABLATION EXPERIMENTS) 2相关知识2.1 自适应权重粒子群优化&#…...
硬件地址反序?用位操作为LED灯序“纠偏”。反转二进制数即可解决
特别有意思,LED的灯序与其硬件地址刚好相反,没办法直接通过加1实现二进制进位的亮灯操作,查了一些资料说用数组和switch实现,觉得太麻烦了,思索良久,就想到了反转二进制数解决这个问题。 reverse_bits( )是…...
A* floyd算法 bellman-ford
求源点到目标点最短距离 排序的里面要加上与目标点一个预估距离,与dj算法差距只有这儿 预估要小于等于真实的最短距离,吸引力要适当 越接近实际距离越快 #include<bits/stdc.h> using namespace std;// 方向向量:上、右、下、左 const vector<int> …...
【数据挖掘】KL散度(Kullback-Leibler Divergence, KLD)
KL散度(Kullback-Leibler Divergence, KLD) 是衡量两个概率分布 P 和 Q之间差异的一种非对称度量。它用于描述当使用分布 Q 逼近真实分布 P 时,信息丢失的程度。 KL散度的数学定义 给定两个离散概率分布 P(x)和 Q(x),它们在相同的…...
Linux shell 进度条
概述 在 Linux Shell 中实现一个简单的进度条可以通过 printf 命令结合特殊字符来实现,以下是一个示例脚本,它模拟了一个从 0% 到 100% 的进度条。 作用 反馈任务进度:让用户直观了解任务执行的进展情况,比如文件拷贝、系统更新…...
ctfshow web刷题记录
RCE 第一题 eval代码执行 : 1、使用system 加通配符过滤 ?csystem("tac%20fl*") ; 2、反字节执行 xxx %20 echo 反字节 3、变量转移 重新定义一个变量 让他代替我们执行 4、伪协议玩法 ?cinclude$_GET[1]?>&1php://filter/readc…...
leetcode日记(101)填充每个节点的下一个右侧节点指针Ⅱ
意料之中有这题,将之前的思路换一下即可,层序遍历的思路将record(记录下一个循环的次数)手动加减。 /* // Definition for a Node. class Node { public:int val;Node* left;Node* right;Node* next;Node() : val(0), left(NULL)…...
大语言模型微调和大语言模型应用的区别?
1. 基本概念 微调(Fine-tuning) 定义:微调是指在预训练大语言模型的基础上,通过在特定领域或任务的数据上进一步训练,从而使模型在该特定任务上表现更优。 目的:适应具体的任务需求,比如法律文…...
Leetcode-131.Palindrome Partitioning [C++][Java]
目录 一、题目描述 二、解题思路 【C】 【Java】 Leetcode-131.Palindrome Partitioninghttps://leetcode.com/problems/palindrome-partitioning/description/131. 分割回文串 - 力扣(LeetCode)131. 分割回文串 - 给你一个字符串 s,请你…...
DeepSeek:开启机器人智能化的革命性突破
引言 在2025年全球机器人产业格局中,中国AI公司深度求索(DeepSeek)凭借开源机器人智能控制系统DeepSeek-R1,正在掀起一场从底层算法到应用生态的技术革命。不同于传统机器人依赖预设程序的局限,DeepSeek通过深度推理能…...
解决load()文件报错zipfile.BadZipFile: File is not a zip file
报错如下图: 有可能是资源没有关闭造成了错误,这个网上已经有很多解决方案了,大家可自行查阅。 如果你在别的地方都没有找到解决问题,那么可能是以下这种情况。 1、描述 我在服务器上的代码load()加载文件时,出现了…...
【Tools】Visual Studio Code安装保姆级教程(2025版)
00. 目录 文章目录 00. 目录01. Visual Studio Code概述02. Visual Studio Code下载03. Visual Studio Code安装04. Visual Studio Code配置05. 附录 01. Visual Studio Code概述 Visual Studio Code(简称 VS Code)是由微软开发的一款免费、开源且跨平台…...
Python库安装报错解决思路以及机器学习环境配置详细方案
文章目录 概要第三方库gdalmahotasgraphviznltk-datalazypredictscikit-surprisenb_extensions 机器学习GPU-torch安装torch_geometric安装ubuntu安装显卡驱动dlib安装torch-cluster、torch-scatter、torch-sparse和torch-geometricYOLOapextensorflow-gpu Python && P…...
ETIMEDOUT 网络超时问题
根据日志显示,你遇到的 **ETIMEDOUT 网络超时问题** 是由于 npm 无法连接到企业内部的 Nexus 仓库(http://192.168.55.12:8001)导致的。以下是具体原因和解决方案: 一、问题根源 Nexus 仓库不可达 日志中所有依赖包均尝试从 h…...
superset部署记录
具备网络条件的,完全可以一键部署,不需要折腾。网络条件不具备时,部署记录留存备查。 1、正常模式 详细介绍参考:【开源项目推荐】Apache Superset——最优秀的开源数据可视化与数据探索平台-腾讯云开发者社区-腾讯云 (tencent.c…...
todolist docker 小工具
参考链接 前排提示 没有中文,可使用浏览器 翻译 前提 安装docker安装docker-compose 下载仓库 git clone https://github.com/JordanKnott/taskcafe进行安装 cd taskcafe docker-compose -p taskcafe up -d服务启动后会监听在 3333 端口上,通过浏览器…...
PowerToys:解锁Windows生产力的终极武器
欢迎来到涛涛聊AI。今天想着把win键和加号的组合键映射为win键和Q键盘。经过搜索发现PowerToys。 在数字化办公的浪潮中,效率是职场人永恒的追求。微软推出的 PowerToys 作为Windows官方系统强化工具,凭借其强大的功能和开源免费的特性,已成为…...
内存管理:
我们今天来学习一下内存管理: 1. 内存分布: 我们先来看一下我们下面的图片: 这个就是我们的内存,我们的内存分为栈区,堆区,静态区,常量区; 我们的函数栈帧开辟消耗的内存就是我们…...
ElementUI 表格中插入图片缩略图,鼠标悬停显示大图
如何在 ElementUI 的表格组件 Table 中插入图片缩略图,通过鼠标悬停显示大图?介绍以下2种方式: 方法1:直接在模板元素中插入 <template><el-table :data"tableData"><el-table-column label"图片…...
从被动响应到主动预见:智能可观测性技术的变革与实践
思维导图 一、引言 🌃 想象一下,在一个深夜 🌙,你的关键业务系统突然出现故障 🚨。传统情况下,你可能会收到大量不相关的告警 📱💬💬💬,然后花费…...
【Linux】五种 IO 模型与非阻塞 IO
🌈 个人主页:Zfox_ 🔥 系列专栏:Linux 目录 一:🔥 重新理解 IO🦋 为什么说网络问题的本质是 I/O 问题?🎀 从数据流动看网络通信🎀 网络 I/O 的瓶颈 …...
从零开始开发纯血鸿蒙应用之无框截图
从零开始开发纯血鸿蒙应用 〇、前言二、元素定位1、理论依据2、使用指导 三、认识 ComponentSnapshot1、get 方法2、获取 ComponentSnapshot 实例 四、实现组件截图1、掌握图片编码能力2、保存到图库3、实现组件截图 〇、前言 截图,或者说截屏,已经是每…...
【商城实战(36)】UniApp性能飞升秘籍:从渲染到编译的深度优化
【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配…...
无人自助空间智能管理系统解决方案(深度优化版)
无人自助空间智能管理系统解决方案(深度优化版) 一、行业痛点与系统价值 传统管理依赖人工: 人工管理模式下,易出现人为失误,如计费错误、资源分配不当等。同时,人工操作效率低下,在高峰时段…...
pycharm配置镜像源【pycharm最新版(23.2.5及以上)方法】
经常遇到pycharm中无法安装或者安装慢的问题,纠结了好久,终于找到这个解决办法了。 为什么要配置镜像源: 因为Python的包管理工具pip一般从PyPI(Python Package Index)下载安装包,但是PyPI位于国外&#x…...
探索ima.copilot:个人知识库搭建的AI新利器
在信息爆炸的时代,知识的积累与管理成为了个人发展的关键。面对海量的科研文献、工作资料和各类信息,如何高效地构建属于自己的知识体系,是许多人面临的挑战。ima.copilot这款AI工具的出现,为解决这一难题提供了新的思路。它凭借强…...
向量数据库技术系列五-Weaviate介绍
一、前言 Weaviate 是由德国公司 SeMI Technologies 开发的开源向量搜索引擎数据库。它结合了向量搜索和图数据库技术,旨在为 AI 应用提供高效的数据存储和检索能力。具有以下的特点: 高性能向量搜索 Weaviate 支持高效的向量索引和近似最近邻&#x…...
1.Qt SDK 的下载和安装
1Qt 下载官⽹: http://download.qt.io/archive/qt/ 2版本自行选择 3下载对应版本的.exe文件 4下载包下载完成 5双击.exe文件,默认下一步,要注册一个qt的账户 6记住程序安装的位置,后面要配置环境变量 7勾3个(组件自行…...
光场中的核心概念:Macro Pixel与SAI的深度解析与实例应用
一、概念详解:从硬件到算法的核心要素 Macro Pixel(宏像素) Macro Pixel是光场相机的硬件核心单元,由微透镜阵列覆盖的一组传感器子像素构成。每个微透镜对应一个宏像素,其子像素分别记录通过该微透镜不同区域的光线方…...
机器学习(吴恩达)
一, 机器学习 机器学习定义: 计算机能够在没有明确的编程情况下学习 特征: 特征是描述样本的属性或变量,是模型用来学习和预测的基础。如: 房屋面积, 地理位置 标签: 监督学习中需要预测的目标变量,是模型的输出目标。如: 房屋价格 样本: 如: {面积100㎡…...
Ubuntu 20.04 + mysql 8 默认密码问题
问题描述: Ubuntu20.04安装完mysql8之后无法登录,不知道密码 mysql -u root 原因: 系统默认自动配置好了用户和强密码 解决办法: sudo cat /etc/mysql/debian.cnf查看该文件 利用上面的user 和 passwd 即可登录mysql mysql …...
私有云大数据部署:从开发到生产(Docker、K8s、HDFS/Flink on K8s)
1. 引言 在企业级大数据架构中,私有云部署成为了许多企业的首选,尤其是对数据安全性、合规性、资源控制要求较高的场景。相比于公有云,私有云具备更强的灵活性,能够根据业务需求进行定制化优化。本文将探讨如何在私有云环境下构建大数据平台,包括 Docker 容器化、Kuberne…...
使用DeepSeek和墨刀AI,写PRD文档、画原型图的思路、过程及方法
使用DeepSeek和墨刀AI,写PRD文档、画原型图的思路、过程及方法 现在PRD文档要如何写更高效、更清晰、更完整? 还是按以前的思路写PRD,就还是以前的样子。 现在AI这么强大,产品经理如何使用DeepSeek写PRD文档,产品经…...
拥有一台云服务器能做什么呢?
拥有一台云服务器就像拥有了一台24小时在线的远程电脑,你可以通过互联网随时随地管理它。它的用途非常广泛,无论是个人学习、开发测试,还是企业级应用部署,都能发挥重要作用。以下是常见的应用场景,按需求分类整理&…...
4大观点直面呈现|直播回顾-DeepSeek时代的AI算力管理
直播回顾 2025年2月21日晚上9点30分, 我们迎来了新春后的首场人工智能三人行宣讲活动。 这场直播聚焦于Deepseek开源大模型, 探讨了其对AI算力、算法、数据及应用带来的新发展。 本次直播邀请到了上海赞奇的总经理俞阁总和速石科技陈琳涛,…...
DBeaver安装步骤及连接数据库
一、DBeaver安装步骤 1、双击安装文件“dbeaver-ce-21.3.3-x86_64-setup.exe”,进入安装界面 2、点击ok》下一步》我接受,选择下图选项,为此所有用户使用 3、点击下一步,将重新弹出安装开始界面 4、点击ok》下一步》我接受&…...