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题 买卖股票的最佳时机 将最大利润拆分为每天的利润之和,仅仅收集每天的正利润…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...

相关类相关的可视化图像总结
目录 一、散点图 二、气泡图 三、相关图 四、热力图 五、二维密度图 六、多模态二维密度图 七、雷达图 八、桑基图 九、总结 一、散点图 特点 通过点的位置展示两个连续变量之间的关系,可直观判断线性相关、非线性相关或无相关关系,点的分布密…...
MyBatis-Plus 常用条件构造方法
1.常用条件方法 方法 说明eq等于 ne不等于 <>gt大于 >ge大于等于 >lt小于 <le小于等于 <betweenBETWEEN 值1 AND 值2notBetweenNOT BETWEEN 值1 AND 值2likeLIKE %值%notLikeNOT LIKE %值%likeLeftLIKE %值likeRightLIKE 值%isNull字段 IS NULLisNotNull字段…...
信息系统分析与设计复习
2024试卷 单选题(20) 1、在一个聊天系统(类似ChatGPT)中,属于控制类的是()。 A. 话语者类 B.聊天文字输入界面类 C. 聊天主题辨别类 D. 聊天历史类 解析 B-C-E备选架构中分析类分为边界类、控制类和实体类。 边界…...
基于Java的离散数学题库系统设计与实现:附完整源码与论文
JAVASQL离散数学题库管理系统 一、系统概述 本系统采用Java Swing开发桌面应用,结合SQL Server数据库实现离散数学题库的高效管理。系统支持题型分类(选择题、填空题、判断题等)、难度分级、知识点关联,并提供智能组卷、在线测试…...
Amazon RDS on AWS Outposts:解锁本地化云数据库的混合云新体验
在混合云架构成为企业数字化转型标配的今天,如何在本地数据中心享受云数据库的强大能力,同时满足数据本地化、低延迟访问的严苛需求?Amazon RDS on AWS Outposts 给出了完美答案——将AWS完全托管的云数据库服务无缝延伸至您的机房࿰…...

【计算机网络】SDN
SDN这种新型网络体系结构的核心思想:把网络的控制层面与数据层面分离,而让控制层面利用软件来控制数据层面中的许多设备。 OpenFlow协议可以被看成是SDN体系结构中控制层面与数据层面之间的通信接口。 在SDN中取代传统路由器中转发表的是“流表”&…...