C++ 智能指针常用总结
C++ 智能指针常用总结
文章目录
- C++ 智能指针常用总结
- 1. 写在对前面
- 2. why 智能指针
- 3. what 智能指针
- 3.1 unique_ptr
- 3.2 shared_ptr
- 3.3 weak_ptr
- 3. how 指针指针
- 3.1 unique_ptr
- 3.1.1 创建
- 3.1.2 成员函数
- 3.2 shared_ptr
- 3.2.1创建
- 3.2.2 成员对象
- 3.3 weak_ptr
- 4. 碎碎念
- 5.参考资料
1. 写在对前面
最近接了一个写 c++ 的任务,在使用智能指针的时候,写出一个 dump 的 bug。作为 golang 小白在入门 c++ 的必经之路。整理一份 c++ 智能指针的常用知识点。全文按照如下思路整理
-
why 智能指针
-
what 智能指针
-
how 智能指针
智能指针是 C++ 中用于管理动态分配对象的一种特殊指针。它能够自动管理对象的生命周期。避免内存泄漏和野指针的问题。
2. why 智能指针
「一种特性的提出,势必是为了解决某类难以解决的问题。」在智能指针提出之前,C++ 是通过手动分配和释放内存来进行管理的。使用如下两个关键字进行分配和释放内存:
-
new:用于分配动态内存。通过使用 new 关键字,可以在堆上分配指定大小的内存,并返回指向新分配的内存的指针。例如,可以使用
int* p = new int;来分配一个整数的内存,并将指向该内存的指针存储在变量 p 中。 -
delete:用于释放动态内存。使用 delete 关键字,可以释放通过 new 关键字分配的内存。例如,可以使用
delete p;来释放变量 p 指向的整数内存。
这种原始的内存管理方式需要程序员手动追踪和管理分配和释放的内存,容易引发一系列问题,比如内存泄漏、重复释放、野指针等。因此,为了简化内存管理的过程并减少错误,C++ 引入智能指针和 RAII (资源获取即初始化) 等方式来自动管理和释放内存。
注:资源获取即初始化(Resource Acquisition Is Initialization,RAII) 是一种 C++ 编程技术,通过在对象的构造函数中获取资源,并在析构函数中释放资源,以确保资源在对象生命周期内始终被争取管理。
RAII 的核心思想,将资源的生命周期与对象的生命周期绑定在一起。当对象被创建时,它获取了所需的资源;当对象被销毁时,它释放了持有的资源。
3. what 智能指针
智能指针有多种实现,包括 std::shared_ptr、std::unique_ptr 和 std::weak_ptr。要理解这三类智能指针,需要从「对象所有权」的概念入手。
-
每个独占的资源都应该有一个所有者 owner。
-
被共享的资源需要通过引用计数来管理。对应的实现是 shared_ptr。
-
借用资源的时候,可以使用 raw point 或者引用。但更推荐使用引用
-
如果有循环引用,需要打破循环,可能的办法有借助 weak pointer。
-
资源的所有权可以被转移,比如 move。
-
资源的所有者负责释放资源,资源的借用者不能释放资源。
指针解决的是第1、2、4 这三类问题。
3.1 unique_ptr
unique_ptr 代表的是专属所有权(exclusive ownership),即由 unique_ptr 管理的内存,只能被一个对象持有。
注:大多数场景下应用到的应该是 unique_ptr
性能:c++ 的 zero cost abstraction 的特点,unique_ptr 在默认的情况下和裸指针的大小是一样的。所以在内存上没有任何的额外消耗,性能最优。
3.2 shared_ptr
shared_ptr 代表共享所有权(shared ownership),即多个 shared_ptr 可以共享同一块内存。
性能
-
「内存占用高」:shared_ptr 的内存占用是裸指针的两倍。因为除了要管理一个裸指针外,还需要维护一个引用计数。
-
「原子操作性能低」:考虑到线程安全问题,引用计数的增减必须是原子操作。而原子操作一般情况下都比非原子操作慢。
-
「使用移动优化性能」:shared_ptr 在性能是虽然低于 unique_ptr。但是可以通过使用 std::move 来优化性能。在「复制 (a = b)」与「移动(a = std::move(b))」之间,建议选择后者。因为移动不用增加引用计数。
3.3 weak_ptr
weak_ptr 解决了 shared_ptr 存在的「循环引用」问题。它不会增加引用计数,也不会阻止资源的销毁。
避免循环引用:在对象之间存在循环引用时,使用 shared_ptr 会到导致资源无法释放。而 weak_ptr 可以打破循环引用,因为它不增加引用计数。当所有的 shared_ptr 释放之后,资源会被争取的释放。
导致循环引用的示例:
class B;
struct A{shared_ptr<B> b;
};
struct B{shared_ptr<A> a;
};
auto pa = make_shared<A>();
auto pb = make_shared<B>();
pa->b = pb;
pb->a = pa;
注:解除循环引用只需要 struct B 中的 shared_ptr(A) 替换为 weak_ptr(A)
性能:weak_ptr 具有比 shared_ptr 更高的性能。但是需要注意的是,由于 weak_ptr 不会增加引用计数,所以如果想要访问对象,需要提前判断对象是否存在,防止访问空指针。
3. how 指针指针
3.1 unique_ptr
3.1.1 创建
- 可以通过如下两种方式,构造出 unique_ptr 类型的空指针
std::unique_ptr<int> p1; // 不传入任何实参
std::unique_ptr<int> p2(nullptr); // 传入空指针
- 在构建 unique_ptr 智能指针时,明确其指向
std::unique_ptr<int> p3(new int(20));
- 使用 std::make_unique 模板函数,可以用于初始化 unique_ptr 智能指针
std::unique_ptr<int> p4 = std::make_unique<int>(10);
注:std::make_unique 为 C++11 标准提供的函数
- 使用 std::move 移动构造函数
std::unique_ptr<int> p5(std::move(p4))
std::unique_ptr<int> p5 = std::move(p4)
注:使用 std::move(p4) 初始化 p5,会使得 p5 拥有 p4 的堆内存,而 p4 则变成空智能指针
- 在初始化 unique_ptr 智能指针时,还可以自定义所指堆内存的释放规则,可以在定义的释放规则中加入自定义的释放规则,以释放默写资源
//指定 default_delete 作为释放规则
std::unique_ptr<int> p6(new int[10], std::default_delete<int[]>());//自定义释放规则
void deleteInt(int*p) {delete []p;
}// lambda 表示缩写 deleteInt 函数
// [](int* p) {delete[]p; }//初始化智能指针,并自定义释放规则
std::unique_ptr<int> p7(new int[10], deleteInt);
特殊说明:unique_ptr 不支持复制和赋值
auto w = std::make_unique<int>();
auto w2 = w; // 编译错误
3.1.2 成员函数
| 成员方法名 | 功 能 |
|---|---|
| operator=() | 重载赋值号 |
| operator*() | 重载 * 号,获取当前unique_ptr 智能指针对象指向的数据。 |
| operator->() | 重载 -> 号,当智能指针指向的数据类型为自定义的结构体时,通过 -> 运算符可以获取其内部的指定成员。 |
| release() | 返回指向管理对象的指针并释放所有权。 |
| reset() | 替换 unique_ptr 管理的对象。 |
| swap() | 交换 2 个相同类型 unique_ptr 智能指针的内容。 |
| get() | 获得 unique_ptr 对象内部包含的普通指针。 |
| get_deleter() | 返回用于销毁管理对象的删除器 |
| operator bool() | 判断当前 unique_ptr 对象是否为空智能指针,如果是空指针,返回 false;反之,返回 true。 |
3.2 shared_ptr
3.2.1创建
-
可以通过如下两种方式,构造出 shared_ptr 类型的空指针
std::shared_ptr<int> p1; // 不传入任何实参 std::shared_ptr<int> p2(nullptr); // 传入空指针 -
在构建 shared_ptr 智能指针时,明确其指向
std::shared_ptr<int> p3(new int(20)); -
使用 std::make_shared 模板函数,可以用于初始化 shared_ptr 智能指针
std::shared_ptr<int> p4 = std::make_shared<int>(10);注:std::make_shared 为 C++11 标准提供的函数
-
使用 std::move 移动构造函数或者使用赋值构造
// 赋值构造 std::shared_ptr<int> p5(p4); std::shared_ptr<int> p5 = p4;// 移动构造 std::unique_ptr<int> p5(std::move(p4)) std::unique_ptr<int> p5 = std::move(p4)注:
使用赋值构造会导致引用计数加 1,同一普通指针不能同时为多个 shared_ptr 对象赋值
使用 std::move(p4) 初始化 p5,会使得 p5 拥有 p4 的堆内存,而 p4 则变成空智能指针
-
在初始化 unique_ptr 智能指针时,还可以自定义所指堆内存的释放规则,可以在定义的释放规则中加入自定义的释放规则,以释放默写资源
//指定 default_delete 作为释放规则 std::shared_ptr<int> p6(new int[10], std::default_delete<int[]>());//自定义释放规则 void deleteInt(int*p) {delete []p; }// lambda 表示缩写 deleteInt 函数 // [](int* p) {delete[]p; }//初始化智能指针,并自定义释放规则 std::shared_ptr<int> p7(new int[10], deleteInt);
3.2.2 成员对象
| 成员方法名 | 功 能 |
|---|---|
| operator=() | 重载赋值号,使得同一类型的 shared_ptr 智能指针可以相互赋值。 |
| operator*() | 重载 * 号,获取当前 shared_ptr 智能指针对象指向的数据。 |
| operator->() | 重载 -> 号,当智能指针指向的数据类型为自定义的结构体时,通过 -> 运算符可以获取其内部的指定成员。 |
| swap() | 交换 2 个相同类型 shared_ptr 智能指针的内容。 |
| reset() | 当函数没有实参时,该函数会使当前 shared_ptr 所指堆内存的引用计数减 1,同时将当前对象重置为一个空指针;当为函数传递一个新申请的堆内存时,则调用该函数的 shared_ptr 对象会获得该存储空间的所有权,并且引用计数的初始值为 1。 |
| get() | 获得 shared_ptr 对象内部包含的普通指针。 |
| use_count() | 返回同当前 shared_ptr 对象(包括它)指向相同的所有 shared_ptr 对象的数量。 |
| unique() | 判断当前 shared_ptr 对象指向的堆内存,是否不再有其它 shared_ptr 对象再指向它。 |
| operator bool() | 判断当前 shared_ptr 对象是否为空智能指针,如果是空指针,返回 false;反之,返回 true。 |
3.3 weak_ptr
weak_ptr 当前的代码库中使用的较少,后续代码用到单独总结其使用场景吧。
4. 碎碎念
虽然现在总结的知识都可以通过 chatgpt 问到,但是「学习」的目的在于自己学习、理解和分析,如果只是单纯的看的话,下次、下下次在遇到可能自己还是一知半解的照抄模式。
-
我永远喜欢我自己,我有勇气敞开心扉去接受一起新的变化和想法,但我永远不会因为别人的反映而惶恐不安,不因外界的评价而自我怀疑,我允许自己被否定,但我无需认同,也不会在意。
-
任何人在任何时间分道扬镳很正常。
-
如果感觉有点累了,那就去喜欢的事物里喘口气。
5.参考资料
-
关于 c++ 智能指针的使用场景。智能指针能完全替代 new\delete 吗?
-
什么是 move? 理解 c++ value categories, move, move in Rust
-
RAII: 如何编写没有内存泄漏的代码 with C++
-
std::unique_ptr - cppreference.com
-
C++11 shared_ptr智能指针(超级详细)
-
C++转换构造函数:将其它类型转换为当前类的类型
-
C++拷贝构造函数(复制构造函数)详解
-
C++ 智能指针的正确使用方式 | 编程沉思录
相关文章:
C++ 智能指针常用总结
C 智能指针常用总结 文章目录 C 智能指针常用总结1. 写在对前面2. why 智能指针3. what 智能指针3.1 unique_ptr3.2 shared_ptr3.3 weak_ptr 3. how 指针指针3.1 unique_ptr3.1.1 创建3.1.2 成员函数 3.2 shared_ptr3.2.1创建3.2.2 成员对象 3.3 weak_ptr 4. 碎碎念5.参考资料 …...
OracleRAC 安装配置过程中的问题
OS RHAS 3.2 DB 9204 在RAC的安装配置过程中,虽然是严格仔细按照文档来实施,但还是出现不少问题,现整理出来。 现象一 : 在节点一安装数据库的时候出现以下错误 [oraclerac1 dbs]$ sqlplus "/nolog"SQL*Plus: Relea…...
基于战争策略优化的BP神经网络(分类应用) - 附代码
基于战争策略优化的BP神经网络(分类应用) - 附代码 文章目录 基于战争策略优化的BP神经网络(分类应用) - 附代码1.鸢尾花iris数据介绍2.数据集整理3.战争策略优化BP神经网络3.1 BP神经网络参数设置3.2 战争策略算法应用 4.测试结果…...
K8s 概念及组件
K8s 的全称为Kubernetes,是一种开源的容器编排平台,用于自动化部署以及扩展和管理容器化的应用程序,它提供了一种容器编排和管理的方式,可以帮助开发人员更轻松的管理容器化的应用程序,并且提供了一种跨多个主机的自动…...
【已解决】java的gradle项目报错org.gradle .api.plugins .MavenPlugin
我的java的gradle项目经常报错org.gradle .api.plugins .MavenPlugin。报错这个问题是因为依赖起冲突了,我在网上试了很多方法都没有效果,折让小编我很是苦恼,不过还好到最后问题还是解决了。 首先要知道你的项目所使用的gradle版本…...
计算机网络-计算机网络体系结构-网络层
目录 一、IPV4 IP数据报格式 *IP 数据报分片 *IPV4地址 分类 网络地址转换(NAT) 二、子网划分与子网掩码 *CIDR *超网 协议 ARP协议 DHCP协议 ICMP协议 三、IPV6 格式 IPV4和IPV6区别 地址表示形式 四、路由选择协议 RIP(路由信息协议) OPSF(开发最短路径优…...
60 最长有效括号
最长有效括号 题目描述题解1 DPstack题解2 stack题解3 DP题解4 左右指针 题目描述 给你一个只包含 ( 和 ) 的字符串,找出最长有效(格式正确且连续)括号子串的长度。 示例 1: 输入:s "(()" 输出࿱…...
第17章 MQ(二)
17.11 RabbitMQ如何保证消息的顺序性 难度:★★ 重点:★★★ 白话解析 其实RabbitMQ是一个先进先出的队列,只要消息进入到队列之后那肯定是顺序的,其实这道题问的点就是在消息进队列之前和出队列之后如何保证顺序性。 1、要保证消息进队列的顺序性实际只需要保证生产者只…...
AV1 视频编码标准资源
AV1 视频编码标准资源 A Progress Report: The Alliance for Open Media and the AV1 Codec Alliance for Open Media(开放媒体联盟/AV1官网) aomanalyzer AOM ANALYZER TEST CLIPS(测试视频) (Download each of the the CIF clips found there, in YUV4MPEG (y4m) format…...
pycharm远程连接miniconda完整过程,以及遇到的问题解决
问题1:no-zero exit code(126) env: ‘/home/user2/miniconda3/envs/ihan/bin/python3’: Too many levels of symbolic links Python interpreter process exited with a non-zero exit code 126 因为选择的新建导致太多软连接,先在服务器上建好虚拟环…...
leetcode:2678. 老人的数目(python3解法)
难度:简单 给你一个下标从 0 开始的字符串 details 。details 中每个元素都是一位乘客的信息,信息用长度为 15 的字符串表示,表示方式如下: 前十个字符是乘客的手机号码。接下来的一个字符是乘客的性别。接下来两个字符是乘客的年…...
【马蹄集】—— 概率论专题:第二类斯特林数
概率论专题:第二类斯特林数 目录 MT2224 矩阵乘法MT2231 越狱MT2232 找朋友MT2233 盒子与球MT2234 点餐 MT2224 矩阵乘法 难度:黄金 时间限制:5秒 占用内存:128M 题目描述 输入两个矩阵,第一个矩阵尺寸为 l…...
spring中基础核心接口总结
理解这几个接口,及其实现类就可以快速了解spring,具体的用法参考其他spring资料 1.BeanFactory最基础最核心的接口 重要的实现类有:XmlBeanFactory,以及ApplicationContext接口下的类 2.Resource接口,可以通用地访问文件资源 1)ClassPathResource:读取…...
最新Tuxera NTFS2024破解版mac读写NTFS磁盘工具
Tuxera NTFS for Mac是一款Mac系统NTFS磁盘读写软件。在系统默认状态下,MacOSX只能实现对NTFS的读取功能,Tuxera NTFS可以帮助MacOS 系统的电脑顺利实现对NTFS分区的读/写功能。Tuxera NTFS 2024完美兼容最新版本的MacOS 11 Big Sur,在M1芯片…...
【标准化封装 SOT系列 】 E SOT-89
〇、SOT-89 这个封装也比较常见,但并不易错。 一、E部分 SOT-89 参数 pin-pin 间距1.5mm body size 4.52.5 二、符合当前标准的典型举例 名称pin 数厂家 body DE矩形 (mm)SOT-894Mini-Circuits – PGA-102 — 4.39/4.62.29/2.59 上图 MiniCircuits 也称DF78…...
【建立单链表:头插法,尾插法;循环列表,带尾指针的循环链表合并(将Tb合并在Ta之后)】
文章目录 一、单链表的基本操作的实现1.建立单链表:头插法----元素插入在链表头部,也叫头插法。2.建立单链表:尾插法----元素插入在链表尾部,也叫尾插法。 二、线性表的链式表示和实现1.循环列表2.带尾指针的循环链表合并…...
图论+线性基高斯消元与主元:1019T2 / P4151
http://cplusoj.com/d/senior/p/SS231019B 相当于图上选一条链和一堆环 考虑dfs生成树。 则链是两条从根出发的链 环是每条返祖边组成的环 所以环和链的异或和可以求出来 链的放到线性基里 然后线性基通过高斯消元求主元(贪心思想,主元可以令那一位…...
Django REST Framework完整教程-RESTful规范-序列化和反序列数据-数据视图
文章目录 1.简介及安装2.案例模型2.1.创建模型2.2.安装mysql必要组件2.3.管理后台转中文2.4.启动后台 3.数据序列化4.RESTful规范4.1.协议、域名和版本4.2.uri(统一资源标识符)4.3.查增删改4.4.过滤信息(Filtering)4.5.状态码(Status Codes&a…...
float、double类型的转化和判断为零问题
1、将字符串转化为float、double 浮点数在内存中的存储机制和整形数据不同,有舍入误差,在计算机中用近似表示任意某个实数。具体来说,这个数由一个整数或定点数(即尾数)乘以某个基数(计算机中通常是2&…...
强大的虚拟机软件 VMware Fusion Pro 13中文最新 for mac
VMware Fusion Pro是一款虚拟化软件,它允许在Mac电脑上同时运行Windows和其他操作系统,而无需重启电脑,它采用了领先的虚拟化技术,能够保证在Mac电脑在同时运行多个操作系统时表现出极高的效率和稳定性。 VMware Fusion Pro具有以…...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
