当前位置: 首页 > backend >正文

C++ 17中的通用类型any

C++ 17中的通用类型any

标准库中any的介绍

在C++中,定义一个变量需要指定该变量的具体类型,如果不确定类型,但是又需要这个变量,就可以使用any类型,基本使用如下:

#include <any>int main()
{std::any a = 10;// 转换为指定类型的数据int b = std::any_cast<int>(a);std::cout << b << std::endl;a = "hello world";std::string s = std::any_cast<std::string>(a);std::cout << s << std::endl;return 0;
}

关于any类型更多的用法参考官方文档

any的实现原理

要实现任意类型,那么少不了的就是用到模版,如果直接写成模版,那么就是下面的写法:

template<class T>
class Any
{};

但是这种写法存在一个问题,因为所谓的任意类型,实际上在创建对象时就无法确定具体类型,而使用模板就意味着定义对象时需要指定类型,这一做法与设计这个类的初衷就是不一致的。基于创建对象时就无法确定具体类型这个问题,那么Any类就不应该使用模版,但是为了存储任意类型,那么就需要在类内创建一个模版类,如下:

class Any
{private:template <class T>class PlaceHolder{};
};

但是上面的写法还有一个问题,既然PlaceHolder是一个模版类,那么在创建对象时需要指定具体类型,也就是说,在Any类就需要存在下面的成员:

PlaceHolder<T> _holder;

但是,类成员要使用模板,那么为了确定类型,类同样需要使用到模板,现在又回到了最开始的问题,Any类不能为模板类,此时可以考虑使用多态来解决,即让PlaceHolder这个模版类继承自一个基类,然后让Any类的成员为基类的指针,这样就可以实现任意类型的存储,如下:

class Any
{
private:class Holder{public:};template <class T>class PlaceHolder : public Holder{};Holder *content_; // Any类保存的数据
};

在父类的Holder中,需要提供一系列纯虚函数,本次提供下面的函数:

class Holder
{
public:virtual ~Holder() = default;// 获取保存内容的类型virtual const std::type_info &getType() = 0;// 克隆当前对象,用于拷贝构造和赋值virtual Holder *clone() = 0;
};

接着,子类中实现这些函数,并提供对应的构造函数:

template <class T>
class PlaceHolder : public Holder
{
public:PlaceHolder(const T &val): val_(val){}// 获取数据类型virtual const std::type_info &getType() override{return typeid(val_);}// 根据具体值构造一个新的父类对象virtual Holder *clone() override{return new PlaceHolder(val_);}T val_;
};

接着,在Any类中,分别实现下面的函数:

=== “无参构造”

// 无参构造
Any(): content_(nullptr)
{
}

=== “基于值的构造”

// 指定类型数据
template <class T>
Any(const T &val): content_(new PlaceHolder(val))
{
}

=== “基于当前类对象的拷贝构造”

// 拷贝构造
// 注意,不能直接通过外部的Any对象content_构造当前的content_
// 此时可能会产生野指针问题
Any(const Any &other): content_(!other.content_ ? nullptr : other.content_->clone())
{
}

=== “移动构造”

// 移动构造
Any(Any &&other)
{swap(other);
}

=== “基于值的赋值重载”

// 重载赋值运算符——针对具体数据
template <class T>
Any &operator=(const T &val)
{// 构造一个匿名对象,直接调用交换函数// 此时外部对象就会与临时对象进行数据交换,交换完成后匿名对象会销毁Any(val).swap(*this);return *this;
}

=== “基于当前类对象的赋值”

// 重载赋值运算符——针对当前类对象
Any &operator=(const Any &other)
{Any(other).swap(*this);return *this;
}

=== “基于当前类对象的移动赋值”

// 移动赋值
Any &operator=(Any &&other)
{Any(std::move(other)).swap(*this);return *this;
}

=== “析构函数”

~Any()
{delete content_;
}

=== “获取保存的内容”

// 获取数值
template <class T>
T *getVal()
{assert(typeid(T) == content_->getType());// 转换为子类对象访问其中的成员return &(dynamic_cast<PlaceHolder<T> *>(content_)->val_);
}

=== “交换函数”

Any &swap(Any &other)
{std::swap(content_, other.content_);return *this;
}

测试代码如下:

class Test
{
public:Test() { std::cout << "构造" << std::endl; }Test(const Test &t) { std::cout << "拷贝" << std::endl; }~Test() { std::cout << "析构" << std::endl; }
};
int main()
{my_any::Any a;{Test t;a = t;}a = 10;int *pa = a.getVal<int>();std::cout << *pa << std::endl;a = std::string("nihao");std::string *ps = a.getVal<std::string>();std::cout << *ps << std::endl;while (1)sleep(1);return 0;
}输出结果:
构造
拷贝
析构
析构
10
nihao
http://www.xdnf.cn/news/10930.html

相关文章:

  • 从“Bucharest”谈起:词语翻译的音译与意译之路
  • Qt 事件传递的完整流程
  • 运维三剑客——awk
  • My Retro App项目开发指南
  • 对 `llamafactory-cli api -h` 输出的详细解读
  • MySQL备份与恢复实战指南
  • 社群营销的一些门道
  • 项目任务,修改svip用户的存储空间。
  • 网络攻防技术八:身份认证与口令攻击
  • 力扣刷题Day 69:搜索二维矩阵(74)
  • Python趣学篇:Pygame重现经典打砖块游戏
  • Axure形状类组件图标库(共8套)
  • 苹果ios系统ipa文件签名的圈外签名是什么稳定性怎么样
  • 力扣4.寻找两个正序数组的中位数
  • isp调试 blend模式指什么
  • VS2022下C++ Boost库安装与使用使用
  • 使用 Python + ExecJS 获取网易云音乐歌曲歌词
  • 01电气设计-380V强电部分设计
  • 前缀和基础训练
  • Docker 镜像(或 Docker 容器)中查找文件命令
  • 5月底 端午节
  • 2024-2025-2-《移动机器人设计与实践》-复习资料-1-7
  • C++语法系列之特殊类设计
  • ​​Agentic Voice Stack 热门项目
  • MySQL连接报SSL错误
  • 【QT】认识QT
  • v4l2常见操作-查看当前摄像头信息,帧率,控制参数,分辨率,支持格式,抓图实践等
  • LangChain核心之Runnable接口底层实现
  • Vue中安装插件的方式
  • [蓝桥杯]路径之谜