C++之Any类的模拟实现

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;
}

输出:

相关推荐
口袋物联2 小时前
设计模式之工厂模式在 C 语言中的应用(含 Linux 内核实例)
linux·c语言·设计模式·简单工厂模式
csbysj20203 小时前
Vue.js 混入:深入理解与最佳实践
开发语言
qq_479875433 小时前
X-Macros(1)
linux·服务器·windows
笨笨聊运维4 小时前
CentOS官方不维护版本,配置python升级方法,无损版
linux·python·centos
Gerardisite4 小时前
如何在微信个人号开发中有效管理API接口?
java·开发语言·python·微信·php
Want5954 小时前
C/C++跳动的爱心①
c语言·开发语言·c++
lingggggaaaa5 小时前
免杀对抗——C2远控篇&C&C++&DLL注入&过内存核晶&镂空新增&白加黑链&签名程序劫持
c语言·c++·学习·安全·网络安全·免杀对抗
phdsky5 小时前
【设计模式】建造者模式
c++·设计模式·建造者模式
H_-H5 小时前
关于const应用与const中的c++陷阱
c++