1.什么是 Any 类?
Any 类(类似于 C++17 的 std::any )是一种灵活的容器,它允许在运行时存储和操作任意类型 的单个值。它的核心价值在于类型擦除 ,使得程序可以在编译时不需要知道具体的数据类型。例如在 C++ 中,容器(如 std::vector)通常需要确定其存储的类型。Any 类打破了这一限制,允许存储来自配置文件的不同类型参数(整数、字符串、布尔值等),在不需要模板的情况下,设计接受和返回任意数据的函数或类。
2.类型擦除和多态
2.1 抽象基类:holder
holder 是实现多态的基础。它是一个抽象类,定义了所有具体存储器必须遵循的接口。
|------------|-----------------------------------------------------------|
| ~holder() | 保证通过基类指针释放内存时,能调用到派生类的析构函数,防止内存泄漏。 |
| type() | 返回存储数据的 typeid 信息。返回引用是为了避免拷贝不可拷贝的 std::type_info 对象。 |
| clone() | 实现多态克隆,是保证 Any 对象具有值语义的关键。 |
cpp
// 抽象基类,用于统一管理不同类型的数据
class holder {
public:
// 虚析构函数,确保派生类对象能被正确销毁
virtual ~holder() {}
// 纯虚函数:获取子对象保存的数据类型信息 (std::type_info)
virtual const std::type_info &type() =0 ;
// 纯虚函数:克隆当前 holder 对象,用于实现深拷贝
virtual holder *clone()=0 ;
};
2.1 抽象基类:placeholder<T>
模板类是真正的存储器。它为每种类型 T 生成一个具体的类,并继承自 holder
cpp
template<class T>
class placeholder: public holder {
public:
// 构造函数:接受一个类型 T 的常量引用,并初始化内部存储的数据
placeholder(const T &val): _val(val) {} // 注意:代码中变量名是 _val,与前面的 T_val 略有不同
// 实现基类的 type() 纯虚函数
// 获取子类对象保存的数据类型 (返回类型 T 的 std::type_info)
virtual const std::type_info &type() { return typeid(T); }
// 实现基类的 clone() 纯虚函数
// 针对当前的对象自身,克隆出一个新的子类对象 (实现深拷贝)
// 注意:这里使用 _val 成员进行构造,前提是 T 具有可用的拷贝构造函数
virtual holder *clone() { return new placeholder(_val); }
public:
// 存储实际数据值 T 的成员变量
T _val; // 注意:代码中变量名是 _val,与前面 T_val 略有不同
};
3.Any类的接口与实现细节
cpp
class Any {
// private 成员,用于存储指向实际数据的指针
holder *_content;
public:
// 默认构造函数
// 初始化 Any 对象为"空"状态,内部指针指向空 (NULL)
Any() : _content(NULL) {}
// 模板构造函数
// 接受任意类型 T 的值,创建一个新的 placeholder 对象来存储它
template<class T>
Any(const T &val) : _content(new placeholder<T>(val)) {}
// 拷贝构造函数 (实现深拷贝)
// 检查源对象 (other) 是否为空,如果不为空,则调用其 _content->clone() 方法进行深拷贝
Any(const Any &other)
: _content(other._content ? other._content->clone() : NULL) {}
// 析构函数
// 释放 _content 指针指向的动态内存
~Any() { delete _content; }
// swap 成员函数 (用于实现高效的赋值操作)
// 交换当前对象和另一个 Any 对象 (_other) 内部的 _content 指针
Any &swap(Any &other) {
// 使用 std::swap 交换两个 holder* 指针
std::swap(_content, other._content);
return *this;
}
// 模板成员函数:获取当前Any对象内部存储的数据(类型T)的指针
// 返回子类对象保存的数据的指针
template<class T>
T *get() {
// 运行时类型检查:
// 想要获取的数据类型 (typeid(T)) 必须和当前 Any 对象实际存储的数据类型 (_content->type()) 一致
assert(typeid(T) == _content->type());
// 类型转换:将 _content (holder*) 强制转换为 (placeholder<T>*)
// 然后访问其内部存储的成员变量 _val,并返回 _val 的地址 (&...)
return &((placeholder<T>*)_content)->_val;
}
// 模板赋值运算符:允许将任意类型 T 的值赋给Any对象
// 赋值运算符的重载函数
template<class T>
Any &operator=(const T &val) {
// Copy-and-Swap 惯用法实现赋值操作:
// 1. 创建一个临时的 Any 对象,它会通过模板构造函数存储 val 的副本。
// 2. 将临时对象和当前对象 (*this) 进行指针交换 (swap)。
// 为 val 构造一个临时的通用容器,然后与当前容器自身进行指针交换。
// 临时对象释放的时候,原先保存的数据也就被释放了 (自动完成清理工作)。
Any(val).swap(*this);
return *this;
}
// 拷贝赋值运算符:允许将另一个 Any 对象赋给当前 Any 对象
Any &operator=(const Any &other) {
// Copy-and-Swap 惯用法:
// 1. 创建一个临时的 Any 对象,通过拷贝构造函数 Any(other) 实现对 other 的深拷贝。
// Any(other) 构造了一个临时的 Any 对象,包含了 other 的数据副本。
// 2. 将临时对象和当前对象 (*this) 进行指针交换 (swap)。
Any(other).swap(*this);
// 3. 交换后,当前对象 (*this) 现在持有 other 的新副本,
// 而临时对象持有当前对象原来的旧数据,临时对象析构时会自动清理旧数据。
return *this;
}
};
4.测试函数
cpp
int main()
{
// 1. 默认构造:创建一个空的 Any 对象
Any a;
// 2. 赋值操作:将整数 10 赋值给 Any 对象 a
// 触发 Any::operator=(const T &val) 模板函数
a = 10;
// 3. 获取数据:调用 get<int>() 获取内部存储的 int 数据的指针
// 注意:这里必须指定正确的类型 int,否则会触发断言 (assert)
int *pa = a.get<int>();
// 4. 打印数据:解引用指针 pa,输出存储的整数值
std::cout << *pa << std::endl; // 输出 10
// 5. 重新赋值:将一个 std::string 对象赋值给 Any 对象 a
// 原来的 int 数据会被自动释放和清理
a = std::string("nihao");
// 6. 再次获取数据:调用 get<std::string>() 获取内部存储的 string 数据的指针
std::string *ps = a.get<std::string>();
// 7. 打印数据:解引用指针 ps,输出存储的字符串
std::cout << *ps << std::endl; // 输出 nihao
// 8. 程序正常退出
return 0;
}
输出:
