Any 的原理以及实现
序言
在 C++17 的更新中引入了一个特别有意思的类型,它提供了一种通用的方式来存储任何类型的数据而不需要提前指定类型, 该类型就是 any。
any 允许你将任意类型的数据存储在一个容器中,并且能够在运行时动态地访问该数据。话不多说,让我们一睹为快吧😊!
首先大家需要注意 any 是 C++17更新的内容,如果大家需要使用,那么至少 17 及以上的版本!
Any 的使用
在刚开始使用 any 时,我总认为他是一个万能类型,但是之后,我更认为它是一种容器,能存储任何类型的值。先简单使用一下吧:
int main()
{std::any A = 1;std::any B = std::string("ABC"); A = B;return 0;
}
可以看到 any 不仅可以接受任意类型,还可以进行不同类型的转化。为什么我说 any 更像是一种容器呢?
首先,一个标准库实现的类型,通常都是会重载流插入流提取,但是 any 不能直接使用:
那我就是想要打印 any 存储的值该怎么办呢?std::any_cast
提供了类型安全的访问方法(不正确的类型访问抛出异常):
其次 any 实现的方法也很像一个容器的操作:
// 查看 any 是否存储了值
A.has_value();// 清空 any 的值
A.reset();
总之,封装到 std::any 中的对象可以是任何类型的对象,只要它是有效的 C++ 类型(例如内置类型、用户自定义类型、类对象等)。
Any 的原理和实现
计算机的世界没有魔法。 any 是怎么实现的类型擦除的呢?大家可能第一时间想到模板。确实,模板帮助了我们进行泛型编程。但是,只是模板肯定是不行的,模板在编译时就确定了一个容器存储的类型,但是我们在执行代码时可以看到:
std::any A = 1;
std::any B = std::string("ABC"); A = B;
在运行时,这里 A 的存储的类型可是从 int -> string,这可不是模板能够做到的。这里还使用到了 多态。
抽象基类
std::any 的核心是一个抽象基类 holder,它定义了存储对象的接口,例如复制和销毁等。所有实际存储对象的类都会继承自这个基类,并实现其接口:
class holder {
public:virtual ~holder() = default; // 析构函数,确保正确释放内存virtual holder* clone() const = 0; // 用于复制virtual void* data() = 0; // 用于访问存储的值virtual const std::type_info& type() = 0; // 存储的值的类型
};
关于多态我们觉得使用水果的例子总是很贴切:我们的抽象基类就像是水果一样,他并没有实体,只是一个概念。但是我们的香蕉,苹果,橘子等就是实打实的水果继承了水果的特性…
模板派生类
派生类不能是特定的类型,而是使用了模板,代表可以接受任意类型:
template <typename T>
class placeholder : public holder {
public:placeholder(const T& value) : _value(value){}placeholder* clone() override {return new placeholder(_value);}void* data() override {return &_value;}const std::type_info& type(){return typeid(_value);}private:T _value;
};
在这里:
_value
: 存储了实际的数据。clone()
: 返回一个新的 placeholder 对象,以支持复制。data()
: 返回存储对象的地址,供 any 获取实际的数据。
现在前置任务已经达成了,就差实现 any 了。
any 类实现
首先该类的成员变量应该是什么呢?我们需要使用 placeholder
接受需要存储的数据,那么成员变量就是 placeholder
咯。肯定不行涩,如果这样,那么我们的类型就固定了,没有达到类型擦除的效果。所以我们需要使用到 holder
:
private:std::unique_ptr<holder> _holder; // 使用智能指针便于内存管理
构造和析构
之后就需要考虑构造函数和析构函数了:
Any() = default; // 无参的template <class T> // 这里需要模板函数接受任意类型Any(const T& val): _val(std::make_unique<placeholder<T>>(val)) // 别忘了传递类型,placeholder 是一个模板类{}~Any() = default; // 智能指针管理内存,方便了很多
拷贝构造
现在基本的框架已经搭好了,准备拓展功能了,先实现拷贝构造和运算符重载吧,在这里我们就直接使用到了 clone
函数,再次构造一个对象然后交给智能指针管理:
Any(const Any& other): _holder(other._holder->clone())
{}
赋值运算符重载
赋值运算符重载直接使用只拷贝传递一个参数,然后将参数的成员变量的值和我们的交换:
Any& operator=(Any other)
{if (&other != this){std::swap(other._holder, _holder);return *this;}
}
这样的操作好处有两个:
- 我们原来存储的值交给局部变量后,局部变量销毁,自动释放我们原来的值
std::swap
会高效地交换两个_holder
,避免了不必要的对象复制
返回保存的值
现在我们最后需要完成返回我们 any 存储的值,这里就要用上我们实现的返回类型了:
template <class T >T& any_cast() {// 判断返回的类型是否合法if (typeid(T) == _holder->type()){return *static_cast<T*>(_holder->data()); // static_cast 更为安全的类型转换}else{throw std::bad_cast();}}
any 虽然实现了类型擦除,但是他背后的开销还是不小的,所以在对于高性能需求的场景,可以考虑是否需要 any 这样的通用类型。
总结
在这篇文章中,我们介绍了 any 的使用,以及具体的实现。any 的实现离不开多态的思想,通过多态,才能够动态地存储不同类型的数据,而不需要在编译时确定数据的具体类型。
相关文章:

Any 的原理以及实现
序言 在 C17 的更新中引入了一个特别有意思的类型,它提供了一种通用的方式来存储任何类型的数据而不需要提前指定类型, 该类型就是 any。 any 允许你将任意类型的数据存储在一个容器中,并且能够在运行时动态地访问该数据。话不多说…...

SQLI LABS | Less-35 GET-Bypass Add Slashes (we dont need them) Integer Based
关注这个靶场的其它相关笔记:SQLI LABS —— 靶场笔记合集-CSDN博客 0x01:过关流程 输入下面的链接进入靶场(如果你的地址和我不一样,按照你本地的环境来): http://localhost/sqli-labs/Less-35/ 话不多说…...

RNN(循环神经网络)详解
1️⃣ RNN介绍 前馈神经网络(CNN,全连接网络)的流程是前向传播、反向传播和参数更新,存在以下不足: 无法处理时序数据:时序数据长度一般不固定,而前馈神经网络要求输入和输出的维度是固定的&a…...

【AI抠图整合包及教程】探索SAM 2:图像与视频分割领域的革新者
在人工智能的浩瀚星空中,Meta公司的Segment Anything Model 2(SAM 2)犹如一颗璀璨的新星,以其前所未有的图像与视频分割能力,照亮了计算机视觉领域的新航道。SAM 2不仅继承了其前身SAM在零样本分割领域的卓越表现&…...

DevExpress中文教程 - 如何使用AI模型检查HTML编辑中的语法?
DevExpress .NET MAUI多平台应用UI组件库提供了用于Android和iOS移动开发的高性能UI组件,该组件库包括数据网格、图表、调度程序、数据编辑器、CollectionView和选项卡组件等。 目前许多开发人员正在寻找多种方法将AI添加到解决方案中(这通常比想象的要…...

python包管理工具pip和conda的使用对比
python包管理工具pip和conda的使用对比 总述1. pip使用2. conda注意虚拟环境之间的嵌套,这个会导致安装包后看不到包,实际是安装到了base环境里 未完待续 总述 pip相对于conda,对应包的依赖关系管理不强,坏处是容易造成包冲突,好…...

Linux案例:DNS服务器配置
Linux案例:DNS服务器配置 实验一:正向解析 服务端配置: [rootserver ~]# setenforce 0 [rootserver ~]# nmcli c modify ens160 ipv4.method manual ipv4.addresses 192.168.70.131/24 ipv4.gateway 192.168.70.2 ipv4.dns 114.114.114.11…...
【Python】__getitem__()方法
getitem() 方法介绍 __getitem__ 方法是 Python 中的一个特殊方法(也被称为魔术方法或特殊方法),用于在类中实现索引访问对象元素的操作。这个方法允许对象实现类似于列表、字典等容器类型的索引操作。当自定义类中定义了 __getitem__ 方法时…...

《Atomic Picnic》进不去游戏解决方法
Atomic Picnic有时候会遇到进不去游戏的情况,这可能是由多种原因造成的,玩家可以采取很多解决方法,比如检查电脑配置、更新系统和驱动或验证游戏文件。 Atomic Picnic进不去游戏怎么办 检查电脑配置 查看自己的电脑配置是否达到了游戏的要求…...

学习日志007--python函数 学完再练习练
函数小练习 一、函数的概念 1.定义 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。 2.作用 函数能提高应用的模块性,和代码的重复利用率 3.定义 函数代码块以 def 关键词开头,后接函数标识符…...

DOM操作和事件监听综合练习——轮播图
下面制作一个如下图所示的轮播图(按Enter键可以控制轮播的开启和关闭,或者点击按钮“第几张”即可跳转到第几张): 下面是其HTML和CSS代码(还没有设置轮播): <!DOCTYPE html> <html …...

nodejs:下载,安装,系统环境配置,更换镜像
下载 地址:https://nodejs.org/zh-cn/download/prebuilt-installer 安装包 开始安装 安装完成 配置环境变量 将原来的用户变量-> Path D:\nodejs\node_global 【系统变量】 添加Path–>变量名:NODE_PATH-> 变量值:D: \…...

【Django】视图函数
【Django】视图函数 视图函数的本质是Python中的函数,视图函数负责处理用户的请求并返回响应,该响应可以是网页的HTML内容、重定向、404错误、XML文档、图像或者任何东西,一般在应用中的views.py编写,示例代码如下: …...

MySQL查询-补充
数据准备: -- 部门表 create table dept(deptno int primary key, -- 部门编号 主键:唯一,非空dname varchar(14), -- 部门名称loc varchar(13) -- 部门地址 );insert into dept values (10,accounting,n…...
【Python Tips】多个条件判断——一种更加简洁清晰的写法
一、引言 在python写条件判断 if 语句时,有时会遇到多种条件的真假判断考虑,比如要同时考虑A和B两个变量的True or False,只有当两者都为真,或都为假,或任意为真为假,再继续处理。此时如果用 if,…...

【Vue】简易博客项目跟做
项目框架搭建 1.使用vue create快速搭建vue项目 2.使用VC Code打开新生成的项目 端口号简单配置 修改vue.config.js文件,内容修改如下 所需库安装 npm install vue-resource --save --no-fund npm install vue-router3 --save --no-fund npm install axios --save …...
【HarmonyOS】PixelMap转化为Uri
【HarmonyOS】PixelMap转化为Uri 问题背景 鸿蒙中的PixelMap类型,其实类似于Android和IOS中的bitmap,是对图片数据信息进行描述的一种逻辑运算使用的图片类型。 而鸿蒙中的Uri类型,本质其实是带file头的文件存储地址,是用来指向…...
【架构论文-2】架构设计中存在的问题和改进方向
一、性能优化相关 当前情况 在高负载情况下,系统的响应时间出现了一定程度的延迟。特别是在业务高峰期,大量并发请求导致部分关键业务模块的处理效率降低,影响了用户体验。改进方向 计划引入性能分析工具对系统进行全面的性能剖析࿰…...
go语言中的结构体含义和用法详解
在Go语言中,结构体(struct)是一种聚合数据类型,可以将多个不同类型的数据组合成一个更复杂的类型。结构体类似于面向对象编程中的“类”,但是Go语言没有类和继承的概念,而是通过结构体和接口实现面向对象编程的特性。 1. 结构体的定义 结构体是一组字段(field)的集合…...
985研一学习日记 - 2024.11.8
一个人内耗,说明他活在过去;一个人焦虑,说明他活在未来。只有当一个人平静时,他才活在现在。 日常 1、起床 2、健身 3、LeetCode刷了2题 买卖股票的最佳时机 将最大利润拆分为每天的利润之和,仅仅收集每天的正利润…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...

汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...

云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...

基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...