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