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

stl中的智能指针类详解

 C++98/03的尝试——std::auto_ptr

 C++11标准废弃了std::auto_ptr(在C++17标准中被移除),取而代之的是std::unique_ptr,

std::auto_ptr容易让人误用的地方是其不常用的复制语义,即当复制一个std::auto_ptr对象时(拷贝复制或 operator=复制),原 std::auto_ptr 对象所持有的堆内存对象也会被转移给复制出来的新std::auto_ptr对象。

std::auto_ptr<int> sp1(new int(8));
std::auto_ptr<int> sp2(sp1);std::auto_ptr<int> sp3(new int(8));
std::auto_ptr<int> sp4;
sp4 = sp3;

在以上代码中分别利用了拷贝构造(sp1=>sp2)和赋值构造(sp3=>sp4)来创建新的std::auto_ptr对象,因此sp1持有的堆对象被转移给sp2,sp3持有的堆对象被转移给sp4。

因为 std::auto_ptr 是不常用的复制语义,所以我们应该避免在 stl 容器中使用std::auto_ptr,当用算法对容器进行操作时(如最常见的容器元素遍历),很难避免不对容器中的元素进行赋值传递,这样便会使容器中的多个元素被置为空指针。

std::unique_ptr

std::unique_ptr 对其持有的堆内存具有唯一拥有权,也就是说该智能指针对资源(即其管理的堆内存)的引用计数永远是 1,std::unique_ptr 对象在销毁时会释放其持有的堆内存。

std::unique_ptr<int> sp1(new int(123));std::unique_ptr<int> sp2;
sp2.reset(new int(123));std::unique_ptr<int> sp3 = std::make_unique<int>(123);
//C++11实现make_unique
template<typename T,typename... Ts>
std::unique_ptr<T> make_unique(Ts&& ...params)
{return srd::unique_ptr<T>(new T(std::forward<Ts>(params)...));
}

std::unique_ptr 禁止复制语义,为了达到这个效果,std::unique_ptr类的拷贝构造函数和赋值运算符(operator=)均被标记为=delete。

template<class T>
class unique_ptr
{//...unique_ptr(const unique_ptr&) = delete;unique_ptr& operator=(const unique_ptr&) = delete;
}std::unique_ptr<int> sp1(std::make_unique<int>(123));
//一下代码无法通过编译
std::unique_ptr<int> sp2(sp1);std::unique_ptr<int> sp3;
sp3 = sp1;

std::unique_ptr具有移动语义

std::unique_ptr<int> func(int val)
{std::unique_ptr<int> up(new int(val));return up;
}
int main()
{std::unique_ptr<int> sp1 = func(123);return 0;
}

以上代码从func函数中得到一个std::unique_ptr对象,然后返回给sp1。既然 std::unique_ptr 不能被复制,那么如何将一个 std::unique_ptr 对象持有的堆内存转移给另外一个呢?答案是使用移动构造。

并不是所有对象的std::move操作都有意义,只有实现了移动构造函数(Move Constructor)或移动赋值运算符(operator=)的类才行,而std::unique_ptr正好实现了二者。

我们可以通过给智能指针自定义资源回收函数来实现资源回收,自定义std::unique_ptr的资源释放函数的语法规则如下:

std::unique_ptr<T,DeletorFuncPtr>

其中,T 是我们要释放的对象类型,DeletorFuncPtr 是一个自定义函数指针。

class Socket
{
public:Socket(){}~Socket(){}//关闭资源句柄void close(){}
};int main()
{auto deletor = [](Socket* pSocket){//关闭句柄pSocket->close();delete pSocket;};std::unique_ptr<Socket,void(*)(Socket* pSocket)> spSocket(new Socket,deletor);
}

std::shared_ptr

std::shared_ptr持有的资源可以在多个std::shared_ptr之间共享,每多一个std::shared_ptr对资源的引用,资源引用计数就会增加1,在每一个指向该资源的std::shared_ptr对象析构时,资源引用计数都会减少1,最后一个 std::shared_ptr 对象析构时,若发现资源计数为 0,则将释放其持有的资源。多个线程之间递增和减少资源的引用计数都是安全的(注意:这不意味着多个线程同时操作std::shared_ptr管理的资源是安全的)。std::shared_ptr提供了一个use_count方法来获取当前管理的资源的引用计数。除了上面描述的内容,std::shared_ptr的用法和std::unique_ptr基本相同。

std::shared_ptr<int> sp1(new int(123));std::shared_ptr<int> sp2;
sp2.reset(new int(123));std::shared_ptr<int> sp3;
sp3 = std::make_shared<int>(123);

std::enable_shared_from_this

在实际开发中有时需要在类中返回包裹当前对象(this)的一个std::shared_ptr对象给外部使用,C++新标准也为我们考虑到了这一点,有如此需求的类只要继承自std::enable_shared_from_this<T>模板对象即可。

class A :public  std::enable_shared_from_this<A>
{
public:A(){std::cout << "A constructor" << std::endl;}~A(){std::cout << "A destructor" << std::endl;}std::shared_ptr<A> getSelf(){return shared_from_this();}
};int main()
{std::shared_ptr<A> sp1(new A());std::shared_ptr<A> sp2 = sp1->getSelf();std::cout << "use count: " << sp1.use_count() << std::endl;return 0;
}

在以上代码中,类A继承自std::enable_shared_from_this<A>并提供了一个getSelf方法返回自身的std::shared_ptr对象,在getSelf方法中调用了shared_from_this方法。

注意事项一:不应该共享栈对象的this指针给智能指针对象。(智能指针管理的是堆对象,栈对象会在函数调用结束后自行销毁,因此不能通过shared_from_this()将该对象交由智能指针对象管理)

注意事项二:std::enable_shared_from_this的循环引用问题。(一个资源的生命周期可以交给一个智能指针对象来管理,但是该智能指针的生命周期不可以再交给该资源来管理。)

std::weak_ptr

std::weak_ptr是一个不控制资源生命周期的智能指针,是对对象的一种弱引用,只提供了对其管理的资源的一个访问手段,引入它的目的是协助std::shared_ptr工作。

std::weak_ptr 可以从一个 std::shared_ptr 或另一个 std::weak_ptr 对象构造,std::shared_ptr可以直接赋值给std::weak_ptr,也可以通过std::weak_ptr的lock函数来获得std::shared_ptr。它的构造和析构不会引起引用计数的增加或减少。std::weak_ptr 可用来解决 std::shared_ptr 相互引用时的死锁问题,即两个 std::shared_ptr 相互引用,这两个智能指针的资源引用计数就永远不可能减少为0,资源永远不会被释放。

#include<iostream>
#include<memory>int mian()
{std::shared_ptr<int> sp1(new int(123));std::cout << "use count: " << sp1.use_count() << std::endl;   //use count: 1std::weak_ptr<int> sp2(sp1);std::cout << "use count: " << sp1.use_count() << std::endl;   //use count: 1std::weak_ptr<int> sp3 = sp1;std::cout << "use count: " << sp1.use_count() << std::endl;   //use count: 1std::weak_ptr<int> sp4 = sp2;std::cout << "use count: " << sp1.use_count() << std::endl;   //use count: 1return 0;
}

无论通过何种方式创建std::weak_ptr,都不会增加资源的引用计数,因此每次输出的sp1的引用计数值都是1。

weak_ptr资源是否有效以及获取管理对象

//m tmpConn 是一个 std::weak ptr<TcpConnection>对象
//m tmpConn 引用的 TcpConnection 已被销毁,直接返回
if(m tmpConn.expired())return;
std::shared ptr<TcpConnection>conn = m tmpConn.lock();
if (conn)
{//省略对 conn的后续操作代码
}

注意:std::weak_ptr 类没有重写 operator->和operator*方法,因此不能像std::shared_ptr 或 std::unique_ptr 一样直接操作对象,std::weak_ptr 类也没有重写 operator bool()操作,因此不能通过std::weak_ptr对象直接判断其引用的资源是否存在。

std::weak_ptr应用场景中的经典例子是订阅者模式或者观察者模式。这里以订阅者为例来说明,消息发布器只有在某个订阅者存在的情况下才会向其发布消息,不能管理订阅者的生命周期。

class Subscriber
{//具体实现....
};
class SubscribeManager
{
public:void publish(){for (const auto& iter : m_subscribers){if (!iter.expired()){}}}
private:std::vector<std::weak_ptr<Subscriber>> m_subscribers;
};

智能指针对象的大小

一个std::unique_ptr对象的大小与裸指针的大小相同(即sizeof(std::unique_ptr<T>)==sizeof(void*)),而 std::shared_ptr 的大小是std::unique_ptr 的两倍。

使用智能指针时的注意事项

1.一旦使用了智能指针管理一个对象,就不该再使用原始裸指针去操作它

2.知道在哪些场合使用哪种类型的智能指针

        在通常情况下,如果我们的资源不需要在其他地方共享,就应该优先使用std::unique_ptr,反之使用 std::shared_ptr。当然,这是在该智能指针需要管理资源的生命周期的情况下进行的;如果不需要管理对象的生命周期,则请使用std::weak_ptr。

3.认真考虑,避免操作某个引用资源已经释放的智能指针

4.作为类成员变量,应该优先使用前置声明(forward declarations)

相关文章:

stl中的智能指针类详解

C98/03的尝试——std&#xff1a;&#xff1a;auto_ptr C11标准废弃了std&#xff1a;&#xff1a;auto_ptr&#xff08;在C17标准中被移除&#xff09;&#xff0c;取而代之的是std&#xff1a;&#xff1a;unique_ptr, std&#xff1a;&#xff1a;auto_ptr容易让人误用的地…...

Linux 阻塞和非阻塞 IO 实验

目录 一、阻塞和非阻塞简介 1、IO 概念 2、阻塞与非阻塞 二、等待队列 1、等待队列头 2、等待队列项 3、将队列项添加/移除等待队列头 4、等待唤醒 5、等待事件 三、轮询 1、应用程序的非阻塞函数 2、Linux 驱动下的 poll 操作函数 四、阻塞IO之等待事件唤醒 添加…...

你要的react+ts最佳实践指南

本文根据日常开发实践&#xff0c;参考优秀文章、文档&#xff0c;来说说 TypeScript 是如何较优雅的融入 React 项目的。 温馨提示&#xff1a;日常开发中已全面拥抱函数式组件和 React Hooks&#xff0c;class 类组件的写法这里不提及。 前沿 以前有 JSX 语法&#xff0c;…...

软件测试人员会被替代吗?IT行业哪个方向的前景最好?字节12年测开是这样说的

互联网测试从业12年&#xff0c;前来作答。 逻辑上来说&#xff0c;软件工程最初始只需要两个岗位&#xff0c;一个是产品经理。&#xff0c;一个是研发&#xff08;开发&#xff09;&#xff0c;剩余的 所有岗位都是由他们衍生而来的。 第三个岗位大概率就是测试&#xff0c…...

十六、vue3.0之富文本编辑器的选择

在工作过程中我们会遇到很多的时候会使用到富文本编辑器,市场上流行的也是各种各样的,那么究竟如何选择呢,今天就给大家讲讲有哪一些,方便大家的选择。 一、TinyMCE TinyMCE 是富文本编辑器领域的头部玩家之一,主流富文本编辑器,功能非常全,你需要的大多数功能它都支持…...

kafka(一) 的架构,各概念

Kafka架构 Kafak 总体架构图中包含多个概念&#xff1a; &#xff08;1&#xff09;ZooKeeper&#xff1a;Zookeeper负责保存broker集群元数据&#xff0c;并对控制器进行选举等操作。 &#xff08;2&#xff09;Producer&#xff1a; 生产者负责创建消息&#xff0c;将消息发…...

【ts的常用类型】

ts的常用类型前言安装ts常见类型原始类型 、数组、 any变量上的类型注解函数对象类型联合类型类型别名接口接口和类型别名的对比前言 typescript中为了使编写的代码更规范&#xff0c;更有利于维护&#xff0c;增加了类型校验&#xff0c;安装 安装 typescript npm i typescr…...

Hyper-V与安卓模拟器不共存

一是某些新的模拟器已经开始使用新接口开发&#xff0c;支持了共存&#xff0c;安装这种新的安卓模拟器即可。 对于不支持共存的模拟器&#xff0c;只得增加一个windows开机后的系统选项&#xff0c;如果需要切换这两种不同选项使用系统&#xff0c;每次切换都需要重启windows系…...

【图像分类】卷积神经网络之ZFNet网络模型结构详解

写在前面: 首先感谢兄弟们的关注和订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 1. 前言 由于AlexNet的提出,大型卷积网络开始变得流行起来,但是人们对于网络究竟为什么能表现的这么好,以及怎…...

亿级高并发电商项目-- 实战篇 --万达商城项目 十三(编写购物车、优化修改商品、下架商品方法、购物车模块监听修改商品、删除商品消息)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是小童&#xff0c;Java开发工程师&#xff0c;CSDN博客博主&#xff0c;Java领域新星创作者 &#x1f4d5;系列专栏&#xff1a;前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶 &#x1f4…...

springboot 虚拟线程demo

jd19支持虚拟线程&#xff0c;虚拟线程是轻量级的线程&#xff0c;它们不与操作系统线程绑定&#xff0c;而是由 JVM 来管理。它们适用于“每个请求一个线程”的编程风格&#xff0c;同时没有操作系统线程的限制。我们能够创建数以百万计的虚拟线程而不会影响吞吐。 做个 spri…...

CTFer成长之路之逻辑漏洞

逻辑漏洞CTF 访问url: http://1b43ac78-61f7-4b3c-9ab7-d7e131e7da80.node3.buuoj.cn/ 登录页面用随意用户名密码登录 访问url&#xff1a; http://1b43ac78-61f7-4b3c-9ab7-d7e131e7da80.node3.buuoj.cn/user.php 登陆后有商品列表&#xff0c;共三个商品,点击购买flag 钱…...

入门力扣自学笔记238 C++ (题目编号:1144)

1144. 递减元素使数组呈锯齿状 题目&#xff1a; 给你一个整数数组 nums&#xff0c;每次 操作 会从中选择一个元素并 将该元素的值减少 1。 如果符合下列情况之一&#xff0c;则数组 A 就是 锯齿数组&#xff1a; 每个偶数索引对应的元素都大于相邻的元素&#xff0c;即 A…...

蓝桥杯-寒假作业

没有白走的路&#xff0c;每一步都算数&#x1f388;&#x1f388;&#x1f388; 题目描述&#xff1a; 有四个等式&#xff0c;每个等式的运算规则已经定好了&#xff0c;也就是我们常见的小学的四则运算&#xff0c;但是能够用来四则运算的数字非常有限&#xff0c;包括1~13…...

测试用例篇

1.测试用例的意义 测试用例&#xff08;Test Case&#xff09;是为了实施测试而向被测试的系统提供的一组集合&#xff0c;这组集合包含&#xff1a;测试环境、操作步骤、测试数据、预期结果等要素。 测试用例的意义是为了帮助测试人员了解测什么&#xff0c;怎么测 eg&#x…...

自动驾驶自主避障概况

文章目录前言1. 自主避障在自动驾驶系统架构中的位置2. 自主避障算法分类2.1 人工势场法&#xff08;APF&#xff09;2.1.1引力势场的构建2.1.2斥力势场的构建2.1.3人工势场法的改进2.2 TEB&#xff08;Timed-Eastic-Band, 定时弹性带&#xff09;2.3 栅格法2.4 向量场直方图(V…...

Python实用的库排名…

Python 是一个功能强大的编程语言&#xff0c;有着丰富的第三方库和模块&#xff0c;可以帮助你解决各种各样的问题。以下是一些比较厉害的 Python 库&#xff1a; NumPy&#xff1a;一个强大的数值计算库&#xff0c;提供了高效的数组和矩阵操作功能。 Pandas&#xff1a;提供…...

【YOLO系列】YOLOv4论文超详细解读1(翻译 +学习笔记)

前言 经过上一期的开篇介绍&#xff0c;我们知道YOLO之父Redmon在twitter正式宣布退出cv界&#xff0c;大家都以为YOLO系列就此终结的时候&#xff0c;天空一声巨响&#xff0c;YOLOv4闪亮登场&#xff01;v4作者是AlexeyAB大神&#xff0c;虽然换人了&#xff0c;但论文中给出…...

【神经网络】Transformer基础问答

1.Transforme与LSTM的区别 transformer和LSTM最大的区别就是LSTM的训练是迭代的&#xff0c;无法并行训练&#xff0c;LSTM单元计算完T时刻信息后&#xff0c;才会处理T1时刻的信息&#xff0c;T 1时刻的计算依赖 T-时刻的隐层计算结果。而transformer的训练是并行了&#xff0…...

制定防火墙策略的步骤和建议

制定防火墙策略是保护企业网络环境安全的关键一步。下面是一些制定防火墙策略的步骤和建议&#xff0c;供参考&#xff1a; 识别网络资产&#xff1a;确定企业网络环境中所有的网络资产&#xff0c;包括服务器、应用程序、数据库、移动设备和终端用户设备等&#xff0c;并进行…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

工程地质软件市场:发展现状、趋势与策略建议

一、引言 在工程建设领域&#xff0c;准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具&#xff0c;正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

MySQL用户和授权

开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务&#xff1a; test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

在Ubuntu24上采用Wine打开SourceInsight

1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...