智能指针之设计模式1

本文探讨一下智能指针和GOF设计模式的关系,如果按照设计模式的背后思想来分析,可以发现围绕智能指针的设计和实现有设计模式的一些思想体现。当然,它们也不是严格意义上面向对象的设计模式,毕竟它们没有那么分明的类层次体系,和GOF经典设计模式在外在形式上有所差别,重点是理解设计模式的思想在它们身上的体现,以及怎样帮助它们实现意图的。

限于篇幅,分成了几篇文章来介绍,先从对象的创建开始。

1、工厂模式

工厂模式是把创建对象和使用对象的职责分离了,它们可以控制对象的创建过程,封装了创建细节,让用户不再关心具体对象的创建过程。

C++标准库提供了一些辅助函数和辅助类来创建智能指针对象,它们都是工厂方法或者工厂类。创建unique_ptr和shared_ptr除了使用常规的构造函数之外,还提供了三个工厂方法:make_shared()、make_unique()和allocate_shared(),它们都是简单工厂方法;此外,为了能够从一个shared_ptr对象的内部,通过this指针创建一个shared_ptr对象,工厂类enable_shared_from_this<T>还提供了成员函数shared_from_this()作为工厂方法。

那么,使用工厂模式创建智能指针对象有什么好处呢?

首先,通过工厂方法可以给创建过程起一个富有表达力、自解释的名称,能有效地帮助程序员容易使用,甚至无需提供阅读文档接口,而类的构造函数必须和类名完全一样,无法能够见文知意。make_shared()和make_unique(),一看就知道是创建shared_ptr和unique_ptr对象,更具特色的是shared_from_this(),通过它的名称就应该知道这个函数是通过this指针来创建shared_ptr对象,既然有this字眼,也能知道应该是在一个类的内部使用。

其次,关注点分离,分离了创建智能指针和使用智能指针的职责,让程序员不再关心智能指针对象的创建过程,不再关心是如何创建出来的,程序员把关注点放在智能指针的使用上面,减轻了程序员的心智负担。有人说,C++有了智能指针之后,就不应该在程序中出现new和delete了,可能说的绝对了点,但显然工厂方法make_shared()和make_unique()给了他这样说的底气。

再者,工厂模式可以控制智能指针对象的创建过程,这也是核心意图,它的作用有下面几点:

1、保证在堆中创建资源对象
智能指针缺省要求管理的资源对象是在堆中创建的对象,如果把一个指向栈上创建的对象的指针,让unique_ptr或shared_ptr去管理,最后在析构时会发生异常,显然是不对的。如何避免程序员无意中犯这样错误?那么作为控制对象创建过程的工厂模式,用在这儿再合适不过了。由工厂方法来控制智能指针对象的创建过程,程序员在make_unique和make_shared工厂函数中只传递创建资源对象相关参数就行了,即保证智能指针管理的肯定是使用new操作符创建的对象,这样就保证了程序的安全性。

2、保证创建过程中资源对象不泄露
make_unique()和make_shared()能够保证资源对象的释放安全,我们稍加留意就会发现,无论是shared_ptr类还是unique_ptr类,在它们的构造函数中都没有同时初始化对象资源,对象资源是在外部使用new操作符在堆上创建之后,以裸指针的形式作为构造函数的参数来创建智能指针对象,也就是说资源对象的创建和智能指针对象的创建,它们不是一体的,它们之间是有空隙的,如果在这个间隙中有别的代码运行,并发生了异常,可能会造成资源泄漏。比如(这个例子来自Effective Modern C++ 条款21):

processWidget(std::shared_ptr<Widget>(new Widget), computePriority());

因为编译器在编译时,可能是按照下面的顺序生成代码:1、实施“new Widget”,2、执行computePriority(),3、运行std::shared_ptr构造函数。如果生成了这样的代码,并且在运行时computePriority()发生了异常,那么在第1步动态分配的Widget对象会被泄露,因为它没有机会被存储到第3步才接管它的shared_ptr对象中去。如果使用make_shared()工厂方法来创建shared_ptr对象,就不会有潜在的资源泄露风险了:

processWidget(make_shared<Widget>(), computePriority());

使用make_unique和make_shared让unique_ptr和shared_ptr在创建对象时就同时获取了资源,即获取资源即初始化(符合了RAII惯例的字面意思)。

3、创建时优化内存空间布局
工厂方法make_shared()在创建shared_ptr对象时,还可以对内存布局进行优化。shared_ptr对象包含了两个指针成员,一个指向资源对象,一个指向控制块,需要进行两次new操作才能初始化完,在访问时需要分别进行两次指针解引用。如果资源对象和控制块分配在同一个内存块中,这样就有更好的空间局部性,对cache更友好。在make_shared()内部可以进行控制这个实现过程,把控制块的大小与资源的大小的和作为分配内存空间的大小,new一次就行了,然后分别让资源对象指针和控制块指针分别指向它们所在的位置,并初始化。让内存空间更紧凑,节省了内存空间,同时因为有更好的cache局部性,也提高了访问速度。

4、保证安全创建智能指针对象
weak_ptr类的lock()成员函数也是创建shared_ptr对象的一个工厂方法,控制的是从一个还没有销毁资源对象的shared_ptr对象中创建另一个shared_ptr对象。在多线程环境下,增加shared_ptr对象的引用计数和把控制块指针、资源对象指针作为参数创建shared_ptr对象时,它们不是原子操作,在多线程下会存在data race。因此,在创建时需要保证线程安全,显然交给程序员在外面实现是不现实的,那就把它封装在一个工厂方法中,让它来控制shared_ptr的创建过程,保证创建过程的线程安全。

enable_shared_from_this<T>类的成员函数shared_from_this()也是创建shared_ptr对象的一个工厂方法,控制的是通过资源对象的this指针来创建shared_ptr对象。它保证了是从一个已有的shared_ptr对象中创建的,如果不是,则会抛出异常;同时也保证了不会发生同一个this指针被多个不同shared_ptr对象管理生存期的错误,否则,如果用户在外部随便把this指针作为参数去调用shared_ptr构造函数,可能是重复管理,而发生错误。

最后,智能指针是裸指针的包装类,而裸指针又指向资源对象,控制创建智能指针对象的过程,实际上也是在控制创建资源对象的过程。例如,工厂模式在控制智能指针对象的创建过程同时,也控制了资源对象使用new在堆上创建,如make_unique()和make_shared(),也控制了shared_ptr对象从this指针创建的过程,如shared_from_this()。

工厂模式控制了智能指针和资源对象的创建过程,那么销毁工作又是如何实现的呢?下一篇文章继续介绍。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/79262.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中间件--ClickHouse-1--基础介绍(列式存储,MPP架构,分布式计算,SQL支持,向量化执行,亿万级数据秒级查询)

1、概述 ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。它由俄罗斯的互联网巨头Yandex为解决其内部数据分析需求而开发&#xff0c;并于2016年开源。专为大规模数据分析&#xff0c;实时数据分析和复杂查询设计&#xff0c;具有高性能、实时数据和可扩展性等…

Go之Slice和数组:深入理解底层设计与最佳实践

在Go语言中&#xff0c;数组&#xff08;Array&#xff09;和切片&#xff08;Slice&#xff09;是两种看似相似却本质不同的数据结构。本文将深入剖析它们的底层实现机制&#xff0c;并结合实际代码示例&#xff0c;帮助开发者掌握核心差异和使用场景。 一、基础概念&#xff…

力扣热题100——普通数组(不普通)

普通数组但一点不普通&#xff01; 最大子数组和合并区间轮转数组除自身以外数组的乘积缺失的第一个正数 最大子数组和 这道题是非常经典的适用动态规划解决题目&#xff0c;但同时这里给出两种解法 动态规划、分治法 那么动态规划方法大家可以在我的另外一篇博客总结中看到&am…

矩阵基础+矩阵转置+矩阵乘法+行列式与逆矩阵

GPU渲染过程 矩阵 什么是矩阵&#xff08;Matrix&#xff09; 向量 &#xff08;3&#xff0c;9&#xff0c;88&#xff09; 点乘&#xff1a;计算向量夹角 叉乘&#xff1a;计算两个向量构成平面的法向量。 矩阵 矩阵有3行&#xff0c;2列&#xff0c;所以表示为M32 获取固…

MySQL之text字段详细分类说明

在 MySQL 中&#xff0c;TEXT 是用来存储大量文本数据的数据类型。TEXT 类型可以存储非常长的字符串&#xff0c;比 VARCHAR 类型更适合存储大块的文本数据。TEXT 数据类型分为以下几个子类型&#xff0c;每个子类型用于存储不同大小范围的文本数据&#xff1a; TINYTEXT: 可以…

超详细!Android 面试题大汇总与深度解析

一、Java 与 Kotlin 基础 1. Java 的多态是如何实现的&#xff1f; 多态是指在 Java 中&#xff0c;同一个行为具有多个不同表现形式或形态的能力。它主要通过方法重载&#xff08;Overloading&#xff09;和方法重写&#xff08;Overriding&#xff09;来实现。 方法重载&a…

如何提高webrtc操作跟手时间,降低延迟

第一次做webrtc项目&#xff0c;操作延迟&#xff0c;一直是个问题&#xff0c;多次调试都不能达到理想效果。偶尔发现提高jitterBuffer时间可以解决此问题。关键代码 const _setJitter (values: number) > { const receives peerConnection.getReceivers();receives.f…

语音合成(TTS)从零搭建一个完整的TTS系统-第一节-效果演示

一、概述 语音合成又叫文字转语音&#xff08;TTS-text to speech &#xff09;&#xff0c;本专题我们记录从零搭建一个完整的语音合成系统&#xff0c;包括文本前端、声学模型和声码器&#xff0c;从模型训练到系统的工程化实现&#xff0c;模型可以部署在手机等嵌入式设备上…

实验三 I/O地址译码

一、实验目的 掌握I/O地址译码电路的工作原理。 二、实验电路 实验电路如图1所示&#xff0c;其中74LS74为D触发器&#xff0c;可直接使用实验台上数字电路实验区的D触发器&#xff0c;74LS138为地址译码器&#xff0c; Y0&#xff1a;280H&#xff5e;287H&…

Linux 使用Nginx搭建简易网站模块

网站需求&#xff1a; 一、基于域名[www.openlab.com](http://www.openlab.com)可以访问网站内容为 welcome to openlab ​ 二、给该公司创建三个子界面分别显示学生信息&#xff0c;教学资料和缴费网站&#xff0c;基于[www.openlab.com/student](http://www.openlab.com/stud…

MyBatis 如何使用

1. 环境准备 添加依赖&#xff08;Maven&#xff09; 在 pom.xml 中添加 MyBatis 和数据库驱动依赖&#xff1a; <dependencies><!-- MyBatis 核心库 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId&g…

ArkTS组件的三个通用(通用事件、通用属性、通用手势)

文章目录 通用事件点击事件 onClick触摸事件 onTouch挂载、卸载事件拖拽事件按键事件 onKeyEvent焦点事件鼠标事件悬浮事件组件区域变化事件 onAreaChange组件尺寸变化事件组件可见区域变化事件组件快捷键事件自定义事件分发自定义事件拦截 通用属性尺寸设置位置设置布局约束边…

智慧城市像一张无形大网,如何紧密连接你我他?

智慧城市作为复杂巨系统&#xff0c;其核心在于通过技术创新构建无缝连接的网络&#xff0c;使物理空间与数字空间深度融合。这张"无形大网"由物联网感知层、城市数据中台、人工智能中枢、数字服务入口和安全信任机制五大支柱编织而成&#xff0c;正在重塑城市运行规…

【python】django sqlite版本过低怎么办

方法一&#xff1a;下载最新版本 复制上面的内容的链接 在服务器上进行操作 wget https://sqlite.org/2025/sqlite-autoconf-3490100.tar.gz tar -zxvf sqlite-autoconf-3490100.tar.gz cd sqlite-autoconf-3490100 ./configure --prefix/usr/local make && make in…

PyTorch - Tensor 学习笔记

上层链接&#xff1a;PyTorch 学习笔记-CSDN博客 Tensor 初始化Tensor import torch import numpy as np# 1、直接从数据创建张量。数据类型是自动推断的 data [[1, 2],[3, 4]] x_data torch.tensor(data)torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])输出&am…

【技术派后端篇】ElasticSearch 实战指南:环境搭建、API 操作与集成实践

1 ES介绍及基本概念 ElasticSearch是一个基于Lucene 的分布式、高扩展、高实时的基于RESTful 风格API的搜索与数据分析引擎。 RESTful 风格API的特点&#xff1a; 接受HTTP协议的请求&#xff0c;返回HTTP响应&#xff1b;请求的参数是JSON&#xff0c;返回响应的内容也是JSON…

从标准九九表打印解读单行表达式的书写修炼(Python)

解读单行表达式书写&#xff0c;了解修习单行捷径。 笔记模板由python脚本于2025-04-16 23:24:17创建&#xff0c;本篇笔记适合喜欢单行喜好python的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值&#xff1a;在于输出思考与经验&#xff0c;而不仅仅是知识的简单复述…

深入解析布尔注入:原理、实战与防御

目录 一、布尔注入的原理与核心逻辑 二、布尔注入的实战步骤 三、关键函数与绕过技巧 四、实战案例&#xff1a;获取数据库名称 五、防御策略与最佳实践 六、总结 一、布尔注入的原理与核心逻辑 布尔注入&#xff08;Boolean-Based Blind SQL Injection&#xff09;是一种…

OpenGL学习笔记(几何着色器、实例化、抗锯齿)

目录 几何着色器爆破物体法向量可视化 实例化&#xff08;偏移量存在uniform中&#xff09;实例化数组&#xff08;偏移量存在顶点属性中&#xff09;小行星带 抗锯齿SSAA&#xff08;Super Sample Anti-aliasing&#xff09;MSAA&#xff08;Multi-Sampling Anti-aliasing&…

idea报错java: 非法字符: ‘\ufeff‘解决方案

解决方案步骤以及说明 BOM是什么&#xff1f;1. BOM的作用2. 为什么会出现 \ufeff 错误&#xff1f;3. 如何解决 \ufeff 问题&#xff1f; 最后重新编译&#xff0c;即可运行&#xff01;&#xff01;&#xff01; BOM是什么&#xff1f; \ufeff 是 Unicode 中的 BOM&#xff0…