C++智能指针及其应用
C++11之后出现了 shared_ptr 和 unique_ptr,这两个类都是基于RAII技术进行设计的
RAII
利用对象生命周期来控制程序资源(如内存,文件句柄,网络连接,互斥量等资源)的技术,具体地说,就是通过构造函数获得资源,通过析构函数释放资源。
RAII的思想需要考虑到一个事实:一个资源不能被释放两次,那么此时如果有两个对象管理同一块资源,这两个对象前后销毁,分别调用析构函数,将导致运行错误。
针对上述可能出现的错误,C++11中有两种方案
方案一
unique_ptr 不允许拷贝或赋值,设计时直接禁用拷贝构造函数和赋值重载函数
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
方案二
在有一些场景中,两个智能指针需要管理同一个资源,此时是通过 shared_ptr 进行实现的
shared_ptr 通过引用计数确定管理一块资源的实例化对象的个数。
引用计数达到的效果:只让最后一个管理资源的对象执行释放资源的逻辑
但是,引用计数带来了很多问题,下面将把这些问题提出,并给出shared_ptr解决问题的设计方案
引用计数的设计方案思考
首先,引用计数不能设计成 shared_ptr 的局部私有成员。如果设计成私有成员,拷贝构造和赋值重载改变的时,无法保证旧对象和新对象同时改变引用计数。
其次,引用计数不能设计成全局变量或者静态bianilin,否则在如下场景中 sp1的_pcount和sp2的_pcount的有着相同地址,这是错误的
class A{
public:A(int a1):_a1(a1){}int _a1 = 1;
};int main(){shared_ptr<A> sp1(new A(1));shared_ptr<A> sp2(new A(2));
}
最后,应该设计一个整型指针变量作为引用计数的变量,而且应该在堆上开辟一块空间,不然这个变量的初始化很麻烦
引用计数带来的问题
记 shared_ptr 中引用计数这个成员变量的名称为 _pcount, 即 int* _pcount;
问题一
在多线程场景下,_pcount 作为一个临界资源,应该如何考虑其线程安全问题?
_pcount作为临界资源的场景:
void func(std::shared_ptr<list<int>> sp, int n){for (int i = 0; i < n; i++){std::shared_ptr<list<int>> copy(sp);sp->emplace_back(i);}
}int main(){std::shared_ptr<list<int>> sp1(new list<int>);thread t1(func, sp1, 1000000);thread t2(func, sp1, 2000000);t1.join();t2.join();
}
从如上代码可以看到,t1 和 t2 这两个线程同时进行同时对 sp 进行尾插,
问题二
在调用赋值重载时,应该如何改变旧对象和新对象中的引用计数?(注意需考虑到如下场景)
class A{
public:A(int a):_a(a){}int _a;
};int main(){std::shared_ptr<A> sp1(new A(1));sp1 = sp1;
}
问题三
循环引用场景应该怎么处理?
循环引用场景如下:
struct Node{std::shared_ptr<Node> _next;std::shared_ptr<Node> _prev;int _val;~Node(){/*析构函数调用时的逻辑*/}
};int main(){std::shared_ptr<Node> p1(new Node);std::shared_ptr<Node> p2(new Node);p1->_next = p2;p2->_next = p1;
}
该场景带来的问题描述:
由于p1->_next的存在,p2 这个对象想要析构,起码要等到 p1 的生命周期结束,因为只有p1的生命周期结束,才会调用其析构函数来释放p1->_next,让引用计数做减减,而由于p2->_next的存在,p1 这个对象想要析构,起码要等到 p2 的生命周期结束,因为只有p2的生命周期结束,才会调用其析构函数来释放p2->_next,
由此看来,p1 和 p2 都只能等对方先析构才能析构,那么最终的结果会导致这两者都不析构
问题四
shared_ptr所管理的资源释放的方式不同,应该怎么设计,使得不同类型的被管理资源都可以通过对应的释放方式进行释放?
考虑如上问题之后,可以设计出如下的 sharedPtr类
#pragma once
#include <atomic>
#include <functional>
using namespace std;template<class T>
class sharedPtr{
public:sharedPtr(T* sharedptr):_shared_ptr(sharedptr),_pcount(new atomic<int>(1)) // 问题一{}template<class D> // 问题四sharedPtr(T* sharedptr, D delMethod):_shared_ptr(sharedptr),_pcount(new atomic<int>(1)) // 问题一,_del(delMethod){}sharedPtr(const sharedPtr<T>& sp):_shared_ptr(sp._shared_ptr),_pcount(sp._pcount){++(*_pcount);}// 问题二,只有左操作数和右操作数的管理的指针不同才进行引用计数的更改// 旧对象的引用计数减一,减到零就释放sharedPtr<T>& operator=(const sharedPtr<T>& sp) {if (_shared_ptr != sp._shared_ptr) {this->release();_shared_ptr = sp._shared_ptr;_pcount = sp._pcount;++(*_pcount);}return *this;}void release() {if (--(*_pcount) == 0) {_del(_shared_ptr); // 问题四delete _pcount;}}int use_count() {return *_pcount;}T* operator->() {return _shared_ptr;}T& operator*() {return *_shared_ptr;}~sharedPtr() {release();}
private:T* _shared_ptr;atomic<int>* _pcount;// 问题四function<void(T*)> _del = [](T* ptr) {delete ptr; };
};
循环引用的解决方案
从以上的分析可以发现,问题三并没有解决,只能通过weak_ptr进行解决,weak_ptr 不属于RAII技术,创建时不会增加引用计数
make_shared解决的问题
当调用以下代码时
auto ptr = std::shared_ptr<MyClass>(new MyClass(args...));
可能会引起内存碎片,中途异常等问题,因为这句代码会被拆成两句话
MyClass* rawPtr = new MyClass(args...);
auto ptr = std::shared_ptr<MyClass> (rawPtr);
由于引用计数和 MyClass 对象在栈上申请的空间不连续,可能引起内存碎片问题,而MyClass在调用构造函数先开辟空间,开辟空间成功但执行逻辑的过程中如果抛异常,将会导致内存泄漏,make_shared 就解决了这个问题
智能指针的应用
如果出现以下场景,则可以使用智能指针:
需要在栈上开辟一段空间,存放一个自定义类型的结构体
注意:这里不考虑这个自定义类型在调用构造函数时是否需要再在栈上申请一段空间
以下是工厂模式的样例代码
#include <iostream>
#include <string>
#include <memory>class Fruit{
public:Fruit(){}virtual void name() = 0;
};class Apple : public Fruit{
public:Apple(){}void name() override{std::cout << "我是苹果!" << std::endl;}
};class Banana : public Fruit{
public:Banana(){}void name() override{std::cout << "我是香蕉!" << std::endl;}
};class FruitFactory{
public:virtual std::shared_ptr<Fruit> create() = 0;
};class AppleFactory : public FruitFactory{
public:std::shared_ptr<Fruit> create() override{return std::make_shared<Apple>();}
};class BananaFactory : public FruitFactory{
public:std::shared_ptr<Fruit> create() override{return std::make_shared<Banana>();}
};int main(){// 先创建一个一个指向FruitFactory的指针// 虽然这里不能创建抽象类的实例,但是可以创建指向抽象类的指针// 这里创建的是一个指向AppleFactory类型的实例的智能指针对象,这个智能指针的类型是FruitFactory// tmp作为一个类型为FruitFactory的指针,可以触发多态,// 不同类中的create将创建出指向不同类型对象的fruitstd::shared_ptr<FruitFactory> tmp(new AppleFactory());std::shared_ptr<Fruit> fruit = tmp->create();fruit->name();tmp.reset(new BananaFactory());fruit = tmp->create();fruit->name();
}
相关文章:
C++智能指针及其应用
C11之后出现了 shared_ptr 和 unique_ptr,这两个类都是基于RAII技术进行设计的 RAII 利用对象生命周期来控制程序资源(如内存,文件句柄,网络连接,互斥量等资源)的技术,具体地说,就是…...

06 算法基础:算法的定义、表现形式(自然语言、伪代码、流程图)、五个特性(有穷性、确定性、可行性、输入、输出)、好算法的设计目标
目录 1 算法的定义 2 算法的三种表现形式 2.1 自然语言 2.2 伪代码 2.3 流程图 3 算法的五个特性 3.1 有穷性 3.2 确定性 3.3 可行性 3.4 输入 3.5 输出 4 好算法的设计目标 4.1 正确性 4.2 可读性 4.3 健壮性 4.4 通用性 4.5 高效率与低存储量 1 算法的定义 …...

【红外传感器】STM32C8T6标准库使用红外对管
好好学习,天天向上 前言一、了解红外二、标准库的代码1.infrared.c2.infrared.h3.main.c4 现象 总结 前言 红外线:频率介于微波与可见光之间的电磁波。 参考如下 【STM32】标准库与HAL库对照学习教程外设篇–红外避障传感器 光电红外传感器详解&#…...

STM32L010F4 最小系统设计
画一个 STM32L010F4 的测试板子...... by 矜辰所致前言 最近需要用到一个新的 MCU: STM32L010F4 ,上次测试的 VL53L0X 需要移植到这个芯片上,网上一搜 STM32L010F4,都是介绍资料,没有最小系统,使用说明等。…...
AI 工具大赏:探索智能时代的得力助手
在当今这个科技飞速发展的时代,人工智能(AI)技术已经深入到我们生活的方方面面。从日常办公到创意设计,从学术研究到娱乐休闲,AI 工具正以其强大的功能和便捷的使用体验,成为人们不可或缺的得力助手。那么&…...

安通物流借助CRM重塑企业客户关系管理新格局
安通控股股份有限公司(以下简称"安通控股")是一家扎根集装箱多式联运物流产业的现代综合物流服务企业,致力于为客户提供绿色、经济、高效、安全的集装箱全程物流解决方案。 据Alphaliner排名统计,截至2023年10月,安通控股综合运力全球排名21位,位居国内内贸集装箱物…...

C++标准模板库--vector
vector 介绍 vector(向量)是一种序列容器,表示为可以改变大小的数组。vector中的元素使用连续的存储位置,这意味着也可以使用指向其元素的常规指针偏移量来访问任意元素,且与数组一样高效。但与数组不同的是ÿ…...

通信学习干货:运营商为什么要大力推广FTTR?
随着数字化时代的来临,互联网的需求不断增长,家庭网络也在不断演进。光纤到家(FTTH)已经成为提供高速互联网连接的标配,但随着技术的发展,我们迎来了FTTR(光纤到房间)技术࿰…...

【Spring篇】初识之Spring的入门程序及控制反转与依赖注入
🧸安清h:个人主页 🎥个人专栏:【计算机网络】,【Mybatis篇】 🚦作者简介:一个有趣爱睡觉的intp,期待和更多人分享自己所学知识的真诚大学生。 文章目录 🎯初始Spring …...
OpenLayers:构建高质量的Web地图应用
OpenLayers:构建高质量的Web地图应用 文章目录 OpenLayers:构建高质量的Web地图应用简介为什么选择 OpenLayers?跨平台兼容性高性能渲染高度可定制化社区支持 安装与设置功能扩展矢量图层地理编码投影转换 交互与事件其他高级特性控制动画数据…...
Java比较两个Excel是否内容一致
领导每天让比较两个Excel中的内容,为了节省工作效率多摸鱼,就写了个java接口,通过上传两个文件 进行代码比较得到详细的比较结果(这个需要自己根据日志二开) 目前只实现了比较功能 话不多说直接上代码,具体看注释 package com.yx…...
UniApp入门教程
UniApp X 是一种用于构建跨平台应用程序的框架,它基于 Vue.js 并通过 UniApp 技术栈支持多种平台,如微信小程序、支付宝小程序、H5、Android 和 iOS。以下是 UniApp X 的一些关键特点和基础知识: UniApp X 的特点 跨平台支持: 可…...

Vue.js中使用Element UI实现动态表单项管理及验证
在Vue.js项目中,表单是与用户交互的重要部分,特别是在需要动态管理表单项的场景下,如何优雅地实现添加、删除、上移、下移及验证功能变得尤为重要。本文将详细介绍如何使用Element UI来实现一个包含动态表单项管理以及验证功能的表单。 效果…...

一插U盘就提示格式化?原因、恢复与预防全攻略
一、现象直击:U盘插入电脑即提示格式化 在日常的工作与生活中,U盘作为重要的数据存储和传输工具,被广泛应用于各类场景。然而,有时当我们满怀期待地将U盘插入电脑时,却会遭遇一个令人头疼的问题——系统弹出提示框&am…...

云电脑使用教程标准版
云电脑,也称为云桌面,是一种通过互联网连接远程服务器,使用虚拟桌面环境来执行计算任务的技术。川翔云电脑通过创建软件镜像,让用户能够快速启动并使用预配置的软件和资料,提供高效且经济的云服务。相较于公有云服务&a…...

浏览器服务端文件下载控制(安全阻止、文件浏览器打开还是下载行为控制)
文章目录 简介Chrome已阻止不安全内容下载PDF直接打开txt、xml、js文件被自动打开了而不是下载阿里OSS设置response header阿里OSS修改metadata 简介 随着浏览器的发展,有很多安全方面的限制,对我们的文件下载行为产生了很大的影响。 在JavaScript下载…...
机器学习——量子机器学习
量子机器学习: 未来的机器学习方法 量子计算和机器学习的结合为计算科学带来了前所未有的前景。量子机器学习(QML)正在迅速发展,目标是利用量子计算的优势来处理传统计算机无法高效解决的问题。本文将深入探讨量子机器学习的基本概念、量子计算的关键技术、具体的量…...

[Linux] 创建可以免密登录的SFTP用户
本文主要包含: 创建新用户创建密钥对用于免密登录新用户将新建用户改造为SFTP用户为SFTP上传数据设置限速 1. 创建新用户 sudo useradd sftp_user sudo passwd sftp_user # 输入密码2. 创建密钥对 参考这篇文章 [Linux] 生成 PEM 密钥对实现服务器的免密登录 3. 将新建用户…...
【部署篇】Redis-03主从模式部署(源码方式安装)
一、准备主机 主从模式只是解决了数据备份容灾并不能解决单点故障问题,生产环境中需要在主从模式基础上增加哨兵,实现主节点宕机时自动将其中一个重节点设置为新的主节点。 主机IP角色说明192.168.128.31master,主节点可读写。192.168.128…...
C/C++语言基础--C++四大类型转换讲解
本专栏目的 更新C/C的基础语法,包括C的一些新特性 前言 通过前面几节课,我们学习了抽象、封装、继承、多态、异常等概念,这一篇我们将继续学习C的类型转换,和C语言还有很大区别的;在本节课最后,也简要说…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...

微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...

C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...