Any类简要实现

cpp 复制代码
#include <iostream>
#include <typeinfo>
#include <cassert>
#include <unistd.h>

class Any
{
private:
    class holder
    {
    public:
        virtual ~holder() {}
        virtual const std::type_info &type() = 0;
        virtual holder *clone() = 0;
    };
    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;
    };
    holder *_content;

public:
    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; }
    Any &swap(Any &other)
    {
        std::swap(_content, other._content);
        return *this;
    }

    template <class T>
    T *get()
    {
        if (_content == nullptr)
            return nullptr;
        assert(typeid(T) == _content->type());
        auto p = static_cast<placeholder<T> *>(_content);
        return &(p->_val);
    }

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

int main()
{
    Any a(100);
    int *p = a.get<int>();
    std::cout << *p << std::endl;

    Any b = std::string("hello any");
    std::string *s = b.get<std::string>();
    std::cout << *s << std::endl;

    Any c;
    c = a;
    std::cout << *c.get<int>() << std::endl;

    // 空对象测试
    Any empty;
    auto pe = empty.get<int>();
    if (pe == nullptr)
    {
        std::cout << "empty object, get return nullptr\n";
    }
    return 0;
}

在C语⾔中,通⽤类型可以使⽤void*来管理,但是在C++中,boost库和C++17给我们提供了⼀个通⽤类型any来灵活使⽤,如果考虑增加代码的移植性,尽量减少第三⽅库的依赖,则可以使⽤C++17特性中的any,或者⾃⼰来实现。⽽这个any通⽤类型类的实现其实并不复杂,以下是简单的部分实现。
Any是经典依靠类型擦除 (Type Erasure)实现,依靠"父类虚函数 + 子类继承",可以在同一个容器里保存任意类型对象(类似std::any底层原理)。
holder:抽象基类

cpp 复制代码
class holder{
public:
    virtual ~holder(){}
    virtual const std::type_info& type() const = 0;
    virtual holder* clone() const = 0;
};

纯虚函数 type() 和 clone(),所以holder是抽象类,不能实例化,只能用来做基类。

虚析构函数 virtual ~holder():继承体系必须要有 ,否则delete _content的时候只会调用父类析构,子类placeholder<T>的内存不会释放,造成内存泄漏。两个虚函数是接口:

  • type():返回保存值的真实类型信息
  • clone():拷贝构造时,新建一个同类型的 placeholder 对象

placeholder<T>:模板子类,public 继承 holder

cpp 复制代码
template<class T>
class placeholder : public holder{
public:
    placeholder(const T& val):_val(val){}
    virtual const std::type_info& type() const override{
        return typeid(T);
    }
    virtual holder* clone() const override{
        return new placeholder(_val);
    }
    T _val; // 真正存储数据的成员
};

继承holder,重写 override 两个虚函数。

每一个不同的T,都会实例化一个独立的placeholder<T>类。placeholder<int>、

placeholder<std::string>是两个完全不同的类,只是都继承自 holder。

_val:保存真实的数据。

Any 类成员 holder* _content;

关键点:父类指针可以指向子类对象 holder*是基类指针,它可以接收任意placeholder<T>子类对象的地址。

_content = new placeholder<int>(100);// _content是holder*,但它实际指向placeholder<int>对象

继承 + 多态如何实现类型擦除

构造 Any a(100)

cpp 复制代码
template<class T>
Any(const T&val):_content(new placeholder<T>(val)){}

new placeholder<int>(100) 创建子类对象,返回placeholder<int>*,隐式向上转型为 holder*,赋值给_content。

向上转型:子类指针 → 父类指针,安全,C++ 继承原生支持。

调用虚函数(多态)

虽然_content类型是holder*,但是运行时看它实际指向的对象类型:

如果指向placeholder<int>,调用placeholder<int>::type()

如果指向placeholder<string>,调用placeholder<string>::type()

clone 拷贝

cpp 复制代码
virtual holder* clone() const override{
    return new placeholder(_val);
}

拷贝 Any 的时候,调用_content->clone(),虚函数会调用对应 placeholder 的 clone,新建一个同类型对象,返回placeholder<T>*,同样向上转型为holder*。

这就是深拷贝的核心,保证Any b = a不会两个 Any 共用同一个_content。

get<T>() 函数

cpp 复制代码
template<class T>
T* get()
{
    if(_content == nullptr)
        return nullptr;
    assert(typeid(T)==_content->type());
    auto p = static_cast<placeholder<T>*>(_content);
    return &(p->_val);
}

_content是holder*(父类指针),我们知道它实际指向 placeholder<T>子类对象 ,于是用static_cast做向下转型:父类指针 → 子类指针。

相关推荐
Run_Teenage1 小时前
算法:树的重心、树的直径
算法
专注仿真1 小时前
高代价计算后置:探测、交战、毁伤三条链路的统一设计哲学
算法·探测·毁伤
小狼Solar1 小时前
SAP MDG 功能范围说明(基于S/4HANA 2025)
java·开发语言
kimnoic1 小时前
Python常用标准库模块及查询使用方法
开发语言·python
在所不辞兄1 小时前
为什么PINN非常适合求解有限元模型
人工智能·深度学习·神经网络·算法·机器学习
知识分享小能手1 小时前
C学习教程,从入门到精通,C语言输入和输出(4)
c语言·开发语言·学习
道尔柯南1 小时前
C++ 入门基础详解:从第一个程序到核心语法特性
开发语言·c++
盛世宏博智慧档案1 小时前
Modbus TCP+PoE以太网温湿度传感器项目部署全流程,附网络调试实操
开发语言·tcp/ip·php·以太网·传感器·温湿度
一棵树73511 小时前
数据结构闲聊
数据结构·算法