Any 的原理以及实现

序言

 在 C++17 的更新中引入了一个特别有意思的类型,它提供了一种通用的方式来存储任何类型的数据而不需要提前指定类型, 该类型就是 any
any 允许你将任意类型的数据存储在一个容器中,并且能够在运行时动态地访问该数据。话不多说,让我们一睹为快吧😊!

 首先大家需要注意 anyC++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 的实现离不开多态的思想,通过多态,才能够动态地存储不同类型的数据,而不需要在编译时确定数据的具体类型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/911690.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

aws申请ssl证书的方法【该证书仅供aws】

这里先声明&#xff0c;过程是对的&#xff0c;最终没有达到目的。 原本想着申请ssl证书替代&#xff0c;结果发现aws证书只能给自己的服务器用 但是整套申请证书以及下载&#xff0c;以及使用aws控制台的过程可以参考借鉴。 起因&#xff1a; 腾讯云的ssl证书越来越没法用了…

HFSS学习笔记(五)金属过孔、复制模型带激励等问题(持续更新...)

HFSS学习笔记&#xff08;五&#xff09;金属过孔、复制模型带激励等问题&#xff08;持续更新…&#xff09; 一、金属过孔设计 方法一&#xff1a;用介质减去金属圆柱体&#xff0c;然后再添加金属圆柱体 方法二&#xff1a;嵌入金属圆柱 圆柱过孔选择材料为“copper” HFS…

996引擎 - 活捉NPC

996引擎 - 活捉NPC 引擎触发 - 引擎事件(QF)事件处理模块 GameEvent测试文件参考资料 引擎触发 - 引擎事件(QF) cfg_game_data 配置 ShareNpc1 可以将QM和机器人的触发事件全部转到 QF 引擎触发是通用的,TXT的所有触发转换成小写后在LUA中就可使用,如说明书中缺省可反馈至对接群…

Vue:侦听属性

Vue&#xff1a;侦听属性 watch深度侦听异步任务 watch 在Vue中&#xff0c;允许用户在数据改变时&#xff0c;做出一定的处理。 语法&#xff1a; new Vue({watch:{属性名:{handler(newValue, oldValue){// 函数体} }} })当一个属性被写入watch中&#xff0c;每当这个属性…

SDL事件相关

文章目录 事件相关的函数和数据结构用户自定义事件代码相关&#xff1a; 事件相关的函数和数据结构 SDL_WaitEvent :等待一个事件SDL_PushEvent 发送一个事件SDL_PumpEvents(): 将硬件设备产生的时间放入事件队列 &#xff0c;用于读取事件&#xff0c;在调用该函数之前&#…

UWB技术在智能资产管理中的应用

超宽带&#xff08;UWB&#xff09;技术是一种高精度、低功耗的定位技术&#xff0c;在现代智能管理系统中得到了广泛的应用。凭借高效的数据传输和精确的定位能力&#xff0c;UWB在资产管理、仓储物流、人员定位等领域展现了巨大的潜力。通过与固定资产管理系统和人员定位系统…

SQL 注入(文件读取)

使用 Grafana&#xff08;一种开源数据可视化和监控解决方案&#xff0c;可推动明智的决策、提高系统性能并简化故障排除&#xff09;通过漂亮的仪表板轻松收集、关联和可视化数据。Grafana 实验性 SQL 表达式功能中的一个 DuckDB SQL 注入漏洞。任何经过身份验证的用户都可以通…

初探鸿蒙:从概念到实践

一、鸿蒙开发的环境准备 开发工具&#xff1a;使用 DevEco Studio&#xff0c;支持 ArkTS 语法。 系统要求&#xff1a;确保计算机符合 DevEco Studio 的最低系统需求。安装步骤&#xff1a;下载 DevEco Studio&#xff0c;安装合适的 SDK 和模拟器 二、鸿蒙应用可以…

《XGBoost算法的原理推导》12-14决策树复杂度的正则化项 公式解析

本文是将文章《XGBoost算法的原理推导》中的公式单独拿出来做一个详细的解析&#xff0c;便于初学者更好的理解。 我们定义一颗树的复杂度 Ω Ω Ω&#xff0c;它由两部分组成&#xff1a; 叶子结点的数量&#xff1b;叶子结点权重向量的 L 2 L2 L2范数&#xff1b; 公式(…

MySQL数据库基础(一) MySQL安装及数据类型

目录 一、MySQL数据裤简介 二、MySQL数据的安装 2.1、MySQL安装 2.2、修改MySQL密码登录策略 三、数据库基础管理 3.1、连接方式及数据储存流程 3.2、库管理命令 3.3、表管理命令 3.4、记录管理命令 四、MySQL数据类型 4.1、常见信息种类 4.2、字符型 4.3、数值型 4.4、日期时间…

《现代工业经济和信息化》是什么级别的期刊?是正规期刊吗?能评职称吗?

​问题解答&#xff1a; 问&#xff1a;《现代工业经济和信息化》是不是核心期刊&#xff1f; 答&#xff1a;不是&#xff0c;是知网收录的正规学术期刊。 问&#xff1a;《现代工业经济和信息化》级别&#xff1f; 答&#xff1a;省级。主管单位&#xff1a;山西省工业和…

SpringCloudAlibabaSidecar整合异构微服务

Spring Cloud Alibaba Sidecar概述 这里引用官方文档的原话&#xff1a; spring-cloud-starter-alibaba-sidecar 是一个用来快速完美整合&#xff08;享受服务发现的优势、有负载均衡、有断路器&#xff09; Spring Cloud 与异构微服务&#xff08;非Spring Cloud应用&#xf…

Vue 组件传递数据-Props(六)

一、Props传递静态数据 defineProps() 和 defineEmits() 为了在声明 props 和 emits 选项时获得完整的类型推导支持&#xff0c;我们可以使用 defineProps 和 defineEmits API&#xff0c;它们将自动地在 <script setup> 中可用&#xff1a; defineProps 和 defineEmits …

SpringBoot源码解析(三)

一、手写模拟springboot核心流程 前面两章内容已经详细解释了springboot的核心原理与启动流程&#xff0c;本章通过手写模拟实现一个SpringBoot&#xff0c;让大家能以非常简单的方式就能知道SpringBoot大概是如何工作的&#xff0c;作为对上两章内容的一个巩固。 一、模拟Sp…

Python实战:调用淘宝API以抓取商品页面数据

在数据驱动的商业决策中&#xff0c;获取电商平台的商品数据至关重要。淘宝作为中国最大的在线购物平台&#xff0c;其商品数据对于市场分析、价格监控和竞品研究等方面都具有极高的价值。本文将通过一个Python实战案例&#xff0c;展示如何调用淘宝API来抓取商品页面的数据。 …

STL---迭代器

本文来源&#xff1a;《C语言程序设计》第10章 理解迭代器对于理解STL框架并掌握STL的使用至关重要。 迭代器是泛化的指针&#xff0c;STL算法利用迭代器对存储在容器中的元素序列进行遍历&#xff0c;迭代器提供了访问容器中每个元素的方法。 虽然指针也是一种迭代器&#…

设计模式-七个基本原则之一-单一职责原则 + SpringBoot案例

单一职责原理:(SRP) 面向对象七个基本原则之一 清晰的职责&#xff1a;每个类应该有一个明确的职责&#xff0c;避免将多个责任混合在一起。降低耦合&#xff1a;通过将不同的职责分开&#xff0c;可以降低类之间的耦合度&#xff0c;提高系统的灵活性。易于维护&#xff1a;当…

京东AI单旋旋转验证码98准确率通杀方案

注意,本文只提供学习的思路,严禁违反法律以及破坏信息系统等行为,本文只提供思路 如有侵犯,请联系作者下架 本文滑块识别已同步上线至OCR识别网站: http://yxlocr.nat300.top/ocr/other/12 京东单旋验证码最近更新了,使用AI生成,要求识别角度,以下是部分数据集: 接下…

playground.tensorflow神经网络可视化工具

playground.tensorflow 是一个可视化工具&#xff0c;用于帮助用户理解深度学习和神经网络的基本原理。它通过交互式界面使用户能够构建、训练和可视化简单的神经网络模型。以下是一些主要的数学模型和公式原理&#xff0c;它们在这个平台中被应用&#xff1a; 1. 线性模型 线…

[vulnhub] DarkHole: 2

https://www.vulnhub.com/entry/darkhole-2,740/ 端口扫描主机发现 探测存活主机&#xff0c;185是靶机 # nmap -sP 192.168.75.0/24 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-11-08 18:02 CST Nmap scan report for 192.168.75.1 Host is up (0.…