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题 买卖股票的最佳时机 将最大利润拆分为每天的利润之和,仅仅收集每天的正利润…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...
