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

智能指针之设计模式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()。

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

相关文章:

智能指针之设计模式1

本文探讨一下智能指针和GOF设计模式的关系&#xff0c;如果按照设计模式的背后思想来分析&#xff0c;可以发现围绕智能指针的设计和实现有设计模式的一些思想体现。当然&#xff0c;它们也不是严格意义上面向对象的设计模式&#xff0c;毕竟它们没有那么分明的类层次体系&…...

Android 中支持旧版 API 的方法(API 30)

Android 中最新依赖库的版本支持 API 31 及以上版本&#xff0c;若要支持 API30&#xff0c;则对应的依赖库的版本就需要使用旧版本。 可通过修改模块级 build.gradle 文件来进行适配。 1、android 标签的 targetSdk 和 compileSdk 版本号 根据实际目标设备的 android 版本来…...

[特殊字符] Hyperlane:Rust 高性能 HTTP 服务器库,开启 Web 服务新纪元!

&#x1f680; Hyperlane&#xff1a;Rust 高性能 HTTP 服务器库&#xff0c;开启 Web 服务新纪元&#xff01; &#x1f31f; 什么是 Hyperlane&#xff1f; Hyperlane 是一个基于 Rust 语言开发的轻量级、高性能 HTTP 服务器库&#xff0c;专为简化网络服务开发而设计。它支…...

【深拷贝、浅拷贝】golang函数参数传递,变量复制后,操作变量参数,是否影响原有数据?全面解析

Golang中深拷贝与浅拷贝的详细解析&#xff0c;以及变量复制、函数参数传递等场景下对新旧变量影响的总结&#xff1a; 一拷贝与浅拷贝的核心区别 1. 浅拷贝&#xff08;Shallow Copy&#xff09; • 定义&#xff1a;仅复制数据的顶层结构&#xff0c;对引用类型字段&#x…...

RIP V2路由协议配置实验CISCO

1.RIP V2简介&#xff1a; RIP V2&#xff08;Routing Information Protocol Version 2&#xff09;是 RIP 路由协议的第二版&#xff0c;属于距离矢量路由协议&#xff0c;主要用于中小型网络环境。相较于 RIP V1&#xff0c;RIP V2 在功能和性能上进行了多项改进&#xff0c…...

《LNMP架构+Nextcloud私有云超维部署:量子级安全与跨域穿透实战》

项目实战-使用LNMP搭建私有云存储 准备工作 恢复快照&#xff0c;关闭安全软件 [rootserver ~]# setenforce 0[rootserver ~]# systemctl stop firewalld搭建LNMP环境 [rootserver ~]# yum install nginx mariadb-server php* -y# 并开启nginx服务并设置开机自启 [r…...

STM32 HAL库 OLED驱动实现

一、概述 1.1 OLED 显示屏简介 OLED&#xff08;Organic Light - Emitting Diode&#xff09;即有机发光二极管&#xff0c;与传统的 LCD 显示屏相比&#xff0c;OLED 具有自发光、视角广、响应速度快、对比度高、功耗低等优点。在嵌入式系统中&#xff0c;OLED 显示屏常被用…...

Excel通过VBA脚本去除重复数据行并保存

一、方法1:使用字典动态去重并保存 适用场景&#xff1a;需要灵活控制去重逻辑&#xff08;如保留最后一次出现的重复项&#xff09;时 Sub 动态去重保存到新表()Dim srcSheet As Worksheet, destSheet As WorksheetDim dict As Object, lastRow As Long, i As LongDim key A…...

大模型Prompt提示词越狱相关知识

大模型Prompt提示词越狱相关知识 一、什么是Prompt提示词越狱&#xff1f; 什么是Prompt提示词 ​ Prompt是指你向AI输入的内容&#xff0c;它直接指示AI该做什么任务或生成什么样的输出&#xff0c;简而言之&#xff0c; Prompt就是你与AI之间的“对话内容”&#xff0c;可…...

3DMAX笔记-UV知识点和烘焙步骤

1. 在展UV时&#xff0c;如何点击模型&#xff0c;就能选中所有这个模型的uv 2. 分多张UV时&#xff0c;不同的UV的可以设置为不同的颜色&#xff0c;然后可以通过颜色进行筛选。 3. 烘焙步骤 摆放完UV后&#xff0c;要另存为一份文件&#xff0c;留作备份 将模型部件全部分成…...

【新人系列】Golang 入门(十三):结构体 - 下

✍ 个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4dd; 专栏地址&#xff1a;https://blog.csdn.net/newin2020/category_12898955.html &#x1f4e3; 专栏定位&#xff1a;为 0 基础刚入门 Golang 的小伙伴提供详细的讲解&#xff0c;也欢迎大佬们…...

Spring Boot 自定义商标(Logo)的完整示例及配置说明( banner.txt 文件和配置文件属性信息)

Spring Boot 自定义商标&#xff08;Logo&#xff09;的完整示例及配置说明 1. Spring Boot 商标&#xff08;Banner&#xff09;功能概述 Spring Boot 在启动时会显示一个 ASCII 艺术的商标 LOGO&#xff08;默认为 Spring 的标志&#xff09;。开发者可通过以下方式自定义&a…...

Ubuntu虚拟机Linux系统入门

目录 一、安装 Ubuntu Linux 20.04系统 1.1 安装前准备工作 1.1.1 镜像下载 1.1.2 创建新的虚拟机 二、编译内核源码 2.1 下载源码 2.2 指定编译工具 2.3 将根文件系统放到源码根目录 2.4 配置生成.config 2.5 编译 三、安装aarch64交叉编译工具 四、安装QEMU 五、…...

【蓝桥杯】2025省赛PythonB组复盘

前言 昨天蓝桥杯python省赛B组比完&#xff0c;今天在洛谷上估了下分&#xff0c;省一没有意外的话应该是稳了。这篇博文是对省赛试题的复盘&#xff0c;所给代码是省赛提交的代码。PB省赛洛谷题单 试题 A: 攻击次数 思路 这题目前有歧义&#xff0c;一个回合到底是只有一个…...

深入解析区块链技术:原理、应用与未来展望

1 区块链技术原理 1.1 基本概念 区块链本质上是一个分布式账本&#xff0c;它由一系列按照时间顺序排列的数据块组成&#xff0c;每个数据块包含了一定时间内的交易信息。这些数据块通过密码学技术相互链接&#xff0c;形成一个不可篡改的链条。其核心特点包括去中心化、不可篡…...

Linux进程替换与自定义shell详解

引言 进程替换和shell编程是Linux系统中极其重要的概念&#xff0c;它们不仅是系统内部工作的基础机制&#xff0c;也是系统管理员和开发者必备的技能。本文将深入探讨Linux中的进程替换原理、系统调用实现以及如何创建自定义shell&#xff0c;帮助你全面理解这些重要概念并掌…...

【数据结构_4下篇】链表

一、链表的概念 链表&#xff0c;不要求在连续的内存空间&#xff0c;链表是一个离散的结构。 链表的元素和元素之间&#xff0c;内存是不连续的&#xff0c;而且这些元素的空间之间也没有什么规律&#xff1a; 1.顺序上没有规律 2.内存空间上也没有规律 *如何知道链表中包…...

Mybatis的简单介绍

文章目录 MyBatis 简介 1. MyBatis 核心特点2. MyBatis 核心组件3. MyBatis 基本使用示例(1) 依赖引入&#xff08;Maven&#xff09;(2) 定义 Mapper 接口(3) 定义实体类(4) 在 Service 层调用 4. MyBatis 与 JPA/Hibernate 对比 MyBatis 简介 MyBatis 是一款优秀的 持久层框…...

JavaScript 性能优化实战:深入探讨 JavaScript 性能瓶颈,分享优化技巧与最佳实践

在当今 Web 应用日益复杂的时代&#xff0c;JavaScript 性能对于用户体验起着决定性作用。缓慢的脚本执行会导致页面加载延迟、交互卡顿&#xff0c;严重影响用户留存率。本文将深入剖析 JavaScript 性能瓶颈&#xff0c;并分享一系列实用的优化技巧与最佳实践&#xff0c;助你…...

1g内存电脑sqlite能支持多少并发

1. SQLite的并发机制 写操作&#xff1a;默认使用串行锁&#xff0c;同一时间仅允许一个写操作&#xff08;其他写/读需等待&#xff09;。读操作&#xff1a;支持多并发读取&#xff0c;但受内存、磁盘I/O和配置限制。 2. 关键限制因素 &#xff08;1&#xff09;内存资源 …...

jetpack之jetpack的概括和其中组件的简单使用

注意⚠&#xff1a;此篇文章由deepseek大力支持&#xff01;&#xff01;&#xff01;(╹ڡ╹ ) 主要是对不知道学什么&#xff0c;对各个组件一头雾水的jetpack新手准备的文章 不知道jetpack学什么&#xff0c;就看这篇文章&#xff01;&#xff01; 1. DataBinding&#xff…...

音视频 五 看书的笔记 MediaCodec

MediaCodec 用于访问底层媒体编解码器框架&#xff0c;编解码组件。通常与MediaExtractor(解封装,例如Mp4文件分解成 video和audio)、MediaSync、MediaMuxer(封装 例如音视频合成Mp4文件)、MediaCrypto、Image(cameraX 回调的ImageReader对象可以获取到Image帧图像,可转换成YU…...

物联网|无人自助台球厅源码|哪些框架支持多设备连接?

在无人自助台球厅的智能化管理中&#xff0c;物联网&#xff08;IoT&#xff09;技术是核心支撑。如何实现不同设备&#xff08;如智能门锁、环境传感器、支付终端、灯光控制系统等&#xff09;的高效连接与协同工作&#xff0c;是系统开发的关键挑战。本文将带大家探讨支持多设…...

Python中NumPy的统计运算

在数据分析和科学计算领域&#xff0c;Python凭借其丰富的库生态系统成为首选工具之一&#xff0c;而NumPy作为Python数值计算的核心库&#xff0c;凭借其高效的数组操作和强大的统计运算功能&#xff0c;广泛应用于机器学习、信号处理、统计分析等场景。本文将系统介绍NumPy在…...

uniapp实现H5页面麦克风权限获取与录音功能

1.权限配置 在uni-app开发H5页面时&#xff0c;需要在manifest.json文件中添加录音权限的配置。具体如下&#xff1a; {"h5": {"permissions": {"scope.record": {"desc": "请授权使用录音功能"}}} }这段配置代码是用于向…...

两个树莓派如何通过wifi direct传输视频并显示

这里写自定义目录标题 在两台设备上安装必要软件Wi-Fi Direct接收端IP&#xff08;自动发现或静态设置&#xff09;设置摄像头参数显示初始化网络设置 系统架构概述 发送端树莓派&#xff1a;捕获视频&#xff08;摄像头或视频文件&#xff09;→ 编码 → 通过Wi-Fi Direct传输…...

ubuntu 系统安装Mysql

安装 mysql sudo apt update sudo apt install mysql-server 启动服务 sudo systemctl start mysql 设置为开机自启 sudo systemctl enable mysql 查看服务状态 &#xff08;看到类似“active (running)”的状态信息代表成功&#xff09; sudo systemctl status mysql …...

selenium快速入门

一、操作浏览器 from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By# 设置选项 q1 Options() q1.add_argument("--no-sandbo…...

Redis:线程模型

单线程模型 Redis 自诞生以来&#xff0c;一直以高性能著称。很多人好奇&#xff0c;Redis 为什么早期采用单线程模型&#xff0c;它真的比多线程还快吗&#xff1f; 其实&#xff0c;Redis 的“快”并不在于并发线程&#xff0c;而在于其整体架构设计极致简单高效&#xff0c;…...

Transformer模型解析与实例:搭建一个自己的预测语言模型

目录 1. 前言 2. Transformer 的核心结构 2.1 编码器&#xff08;Encoder&#xff09; 2.2 解码器&#xff08;Decoder&#xff09; 2.3 位置编码&#xff08;Positional Encoding&#xff09; 3. 使用 PyTorch 构建 Transformer 3.1 导入所需的模块&#xff1a; 3.2 定…...