我们自己实现的阉割版 Any 类核心能力:用一个类存储任意类型数据(int/自定义类/字符串等) ,底层依靠 抽象基类+模板子类 实现类型擦除。
模块1:头文件包含
cpp
#include <iostream> // 输入输出
#include <typeinfo> // 类型识别:typeid 运算符核心依赖
#include <utility> // 标准库 swap 函数
#include <cassert> // 断言:类型不匹配时报错
讲解
这四个头文件是实现 Any 类的基础:
typeinfo:提供typeid,用于类型安全校验;utility:提供std::swap,实现高效交换;cassert:断言,防止非法类型获取数据,保证程序安全。
模块2:内部抽象基类 holder(类型擦除核心接口)
cpp
class holder
{
public:
virtual ~holder(){} // 虚析构函数
virtual const std::type_info& type() =0; // 纯虚函数:获取类型
virtual holder* clone()=0; // 纯虚函数:克隆对象
};
讲解
- 定位 :
Any类的私有内部抽象类,对外完全隐藏; - 虚析构:必须加!保证多态删除子类对象时,析构函数能被正确调用,防止内存泄漏;
- 纯虚函数 :
type():返回存储数据的类型信息;clone():深拷贝对象,支持Any的拷贝/赋值;
- 作用 :定义统一接口 ,屏蔽具体类型差异,是类型擦除的基石。
模块3:模板子类 placeholder<T>(真正存储数据的地方)
cpp
template<class T>
class placeholder:public holder
{
public:
placeholder(const T&val):_val(val){}
virtual const std::type_info& type(){return typeid(T);}
virtual holder* clone(){return new placeholder(_val);}
public:
T _val; // 真实存储数据的变量
};
讲解
- 模板类 :
T是任意类型(int/Test/string...),真正实现存储任意数据; - 继承
holder:满足多态要求,对外统一表现为holder类型; - 构造函数 :初始化成员变量
_val,存入数据; - 重写纯虚函数 :
type():返回当前模板类型T的信息;clone():创建自身副本,实现深拷贝;
- 核心 :
_val是真实数据存储位置,外部完全不可见。
模块4:Any 类核心成员 + 构造/析构函数
cpp
holder*_content; // 核心指针
// 默认构造:空对象
Any():_content(nullptr){}
// 模板构造函数:接收任意类型数据
template<class T>
Any(const T&val):_content(new placeholder<T>(val)){}
// 拷贝构造函数:深拷贝
Any(const Any&other):_content(other._content?other._content->clone():nullptr)
{}
// 析构函数:自动释放内存
~Any(){delete _content;}
讲解
- 核心成员
_content:
holder基类指针,多态指向placeholder<T>子类对象,实现类型擦除; - 默认构造 :创建空的
Any对象; - 模板构造 :接收任意类型
T,创建placeholder<T>对象并赋值给指针; - 拷贝构造 :调用
clone()实现深拷贝,避免浅拷贝指针悬空; - 析构函数 :RAII 机制,自动释放内存,无需手动管理指针。
模块5:交换函数 swap(异常安全核心)
cpp
Any&swap(Any&other)
{
std::swap(_content,other._content);
return *this;
}
讲解
- 作用 :避免自赋值,先构造一个临时对象,再交换交换两个
Any对象的内部指针; - 价值 :是拷贝交换法 的基础,让赋值运算符异常安全、无内存泄漏、无需处理自赋值;
- 极简高效:仅交换指针,不拷贝数据,性能极高。
模块6:数据获取函数 get<T>()(类型安全)
cpp
template<class T>
T*get()
{
assert(typeid(T)==_content->type());
return &((placeholder<T>*)_content)->_val;
}
讲解
- 模板函数 :指定要获取的类型
T; - 断言校验 :类型不匹配直接终止程序,保证类型安全;
- 类型强转 :将基类指针强转为子类指针,获取真实数据
_val; - 返回值:返回数据指针,可读写内部数据。
模块7:赋值运算符重载(两种赋值方式)
cpp
// 赋值1:任意类型直接赋值给 Any
template<class T>
Any& operator=(const T&val)
{
Any(val).swap(*this);
return *this;
}
// 赋值2:Any 对象之间赋值
Any& operator=(const Any&other)
{
Any(other).swap(*this);
return *this;
}
讲解
- 统一用法 :拷贝交换法,C++ 赋值运算符最优写法;
- 原理 :
① 先创建临时对象Any(val)/Any(other);
② 交换临时对象和当前对象的指针;
③ 临时对象析构,自动释放旧内存; - 优点:避免自赋值问题,异常安全, 无内存泄漏,代码极简
模块8:测试类 Test(验证生命周期)
cpp
class Test
{
public:
Test(){ std::cout<<"构造"<<std::endl;}
Test(const Test&t){ std::cout<<"拷贝构造"<<std::endl;}
~Test(){ std::cout<<"析构"<<std::endl;}
};
讲解
自定义测试类,通过打印构造/拷贝构造/析构日志,验证:
Any类能正常存储自定义类型;- 内存管理正确,无泄漏;
- 拷贝/赋值行为符合预期。
模块9:主函数测试(功能验证)
cpp
int main()
{
Any a;
{
Test t;
a=t;
}
a=10;
int *pa=a.get<int>();
std::cout<<*pa<<std::endl;
return 0;
}
讲解
- 测试空对象初始化;
- 测试自定义类型赋值;
- 测试局部对象生命周期;
- 测试int 类型赋值 + 获取数据;
- 验证全程内存自动管理,无泄漏。
核心总结
- holder:抽象接口,定义统一规范;
- placeholder:模板子类,真正存数据;
- _content:基类指针,多态+类型擦除核心;
- 拷贝交换法:赋值运算符最优解;
- RAII:构造申请内存,析构自动释放。