目录

1.通用类型设计思想

1.2.类型擦除 + 继承

1.3.简易实现

1.4.typeid

按参数类型分类

多态 vs 非多态行为对比

1.5.type_info

公开成员函数

特点

限制和注意事项

2.C++17 Any

2.1.构造函数与析构函数

2.2. 赋值操作符

2.3.修改操作

2.4.观察操作

2.5. 非成员函数

2.6.使用建议


  1. 每个网络连接都需要拥有一个用于请求接收与解析的上下文
  2. 上下文的类型或结构不能固定,因为服务器支持的协议可能会不断扩展。不同协议往往需要各自不同的上下文结构
  3. 因此,必须有一种容器,能够保存各种不同类型的结构数据

综上,我们需要一个通用类型 Any,来实现这一目标

1.通用类型设计思想

template <class T>
class Any {
private:
    T _content;
};

1.1.初始思路:直接使用模板,但这种方式的问题在于:实例化时必须明确指定类型

Any<int> a;   // 只能保存 int

而我们真正想要的是:

Any a;
a = 10;      // 保存 int
a = "abc";   // 保存字符串

1.2.类型擦除 + 继承

嵌套设计一个基类 holder,再通过模板子类 placeholder 保存具体类型的数据,Any 类中只保存基类指针

class Any {
private:
    class holder {
        // 基类,用于类型擦除
    };

    template <class T>
    class placeholder : public holder {
    public:
        T _val;
        placeholder(const T& val) : _val(val) {}
    };

    holder* _content;  // 指向具体类型的 placeholder
};

当 Any 需要保存某个数据时,只需实例化对应的 placeholder<T> 子类对象,让该子类对象实际持有数据,而 Any 通过基类指针来统一管理

1.3.简易实现

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 ~holder(){} 
        
        //获取子类对象保存的数据类型
        virtual const std::type_info& type() { return typeid(T); };

        //针对当前的自身对象,克隆出一个新的子类对象
        virtual holder* clone() { return new placeholder<T>(_val); }
    public:
        T _val;
    };
    holder* _content;

public:
    Any() :_content(nullptr){}

    template<class T>
    Any(const T& val) :_content(new placeholder<T>(val)){}
    //为什么要单独写                                    //只能这样调用,palceholder不是any的成员,只能通过指针
    Any(const Any& other) :_content(other._content ? other._content->clone() : nullptr){}
    ~Any() { delete _content; }

    //返回子类对象保存的数据的指针
    template<class T>
    T* get()
    {
        //返回类型要一致,不能隐式类型转换等
        assert(typeid(T) == _content->type());

        return &(static_cast<placeholder<T>*>(_content)->_val);
    }

    //交换,支持连续的交换
    Any& MySwap(Any& other)
    {
        std::swap(_content, other._content);

        return *this;
    }

    template<class T>
    Any& operator=(const T& val)
    {
        //临时对象,自动析构
        Any(val).MySwap(*this);

        return *this;
    }

    Any& operator=(const Any& other)
    {
        Any(other).MySwap(*this);

        return *this;
    }
};

(1)模板构造函数

    template<class T>
    Any(const T& val) : _content(new placeholder<T>(val)) {}
    1. 根据 val 的类型 T,实例化 placeholder<T>
    2. 用 val 初始化 placeholder 的 _val 成员
    3. 将 _content 指向这个新创建的对象

    (2)拷贝构造函数 - 深拷贝

    Any(const Any& other) 
        : _content(other._content ? other._content->clone() : nullptr) {}

    如果只是拷贝指针,析构时 double free!程序崩溃

    (3)operator=(const T& val)

    template<class T>
    Any& operator=(const T& val)
    {
        Any(val).MySwap(*this);  // 关键!
        return *this;
    }
    1. Any(val) 创建临时对象
    2. 临时对象与 *this 交换内容
    3. 临时对象析构,释放原内容

    (4)delete _content 能正确释放

    ~Any() { delete _content; }
    1. holder 有虚析构函数(virtual ~holder())
    2. delete _content 会正确调用 placeholder<int> 的析构函数
    3. 然后释放 placeholder 对象占用的内存

    1.4.typeid

    #include <typeinfo>
    
    typeid(类型)        // 编译时确定
    typeid(表达式)      // 可能运行时确定(多态类型)

    typeid 是 C++ 的关键字,用于在运行时获取类型信息,返回 const std::type_info&

    按参数类型分类

    参数类型 求值行为 返回值 示例
    类型名 编译时 该类型的 type_info typeid(int)
    非多态类型表达式 编译时(不求值) 表达式静态类型的 type_info typeid(10 + 20)
    多态类型表达式 运行时(求值) 对象动态类型的 type_info typeid(*ptr)
    多态类型空指针 运行时(抛出异常) 抛出 std::bad_typeid typeid(*nullptr)

    多态 vs 非多态行为对比

    类型分类 条件 typeid(指针) typeid(*指针)
    非多态 无虚函数 指针静态类型 指针静态类型(不识别派生类)
    多态 有虚函数 指针静态类型 指向对象的动态类型(识别派生类)

    1.5.type_info

    std::type_info 是 C++ 运行时类型信息 (RTTI)系统的核心类,用于存储和比较类型信息

    公开成员函数

    方法 功能 返回值 示例
    bool operator==(const type_info& rhs) const noexcept 比较两个类型是否相同 相同返回 true typeid(int) == typeid(int)
    bool operator!=(const type_info& rhs) const noexcept 比较两个类型是否不同 不同返回 true typeid(int) != typeid(double)
    bool before(const type_info& rhs) const noexcept 检查当前类型是否排在 rhs 之前(实现定义的排序) 是返回 true typeid(int).before(typeid(double))
    const char* name() const noexcept 返回类型的实现定义名称 以空字符结尾的字符串 cout << typeid(int).name()
    size_t hash_code() const noexcept (C++11) 返回类型的哈希值 哈希值(可作 unordered_map 的键) size_t h = typeid(int).hash_code()

    特点

    特性 说明
    不可拷贝 拷贝构造函数和赋值运算符被删除
    不可取地址 指向 type_info 的指针可能不稳定
    生命周期 程序整个生命周期内有效
    唯一性 同一类型在整个程序中有唯一的 type_info 对象

    限制和注意事项

    限制 说明 示例
    不可拷贝/赋值 type_info 对象不可拷贝或赋值 type_info copy = typeid(int); // wrong
    仅存储指针/引用 通常存储指针或引用 const type_info& ref = typeid(int);
    多态要求 对指针解引用需要虚函数才能获得动态类型 typeid(*ptr) 需要基类有虚函数
    实现定义名称 name() 返回值因编译器而异 需要平台特定解码
    before() 顺序 严格弱排序,但排序规则是实现定义的 可用于 map 但不能假设特定顺序
    静态类型 非多态类型返回静态类型 typeid(ptr) 返回指针类型

    2.C++17 Any

    2.1.构造函数与析构函数

    构造函数 描述 示例
    any() noexcept 默认构造一个空对象 std::any a;
    any(const any& other) 拷贝构造函数(深拷贝) std::any a2(a1);
    any(any&& other) noexcept 移动构造函数 std::any a2(std::move(a1));
    template<class T> any(T&& value) 类型转换构造(完美转发) std::any a = 42;
    ~any() 析构函数,释放资源 自动调用

    2.2. 赋值操作符

    操作符 描述 示例
    any& operator=(const any& other) 拷贝赋值 a2 = a1;
    any& operator=(any&& other) noexcept 移动赋值 a2 = std::move(a1);
    template<class T> any& operator=(T&& value) 从值赋值(完美转发) a = 42;

    2.3.修改操作

    方法 描述 示例
    void reset() noexcept 销毁所含对象,变为空 a.reset();
    void swap(any& other) noexcept 交换两个 any 对象的内容 a1.swap(a2);

    2.4.观察操作

    方法 描述 示例
    bool has_value() const noexcept 检查是否包含值 if (a.has_value())
    const std::type_info& type() const noexcept 获取存储类型的 type_info if (a.type() == typeid(int))

    2.5. 非成员函数

    函数 描述 示例
    void swap(any& a, any& b) noexcept 交换两个 any 对象 std::swap(a1, a2);
    template<class T> T any_cast(const any& operand) 值转换(拷贝),失败抛出 std::bad_any_cast int val = std::any_cast<int>(a);
    template<class T> T* any_cast(any* operand) noexcept 指针转换,失败返回 nullptr int* p = std::any_cast<int>(&a);
    template<class T> T any_cast(any&& operand) 移动转换 int val = std::any_cast<int>(std::move(a));
    template<class T> T* any_cast(const any* operand) noexcept const 指针转换 const int* p = std::any_cast<int>(&a);

    2.6.使用建议

    1. 优先使用指针版本的 any_cast:避免异常开销
    2. 避免频繁拷贝大对象:使用移动语义或指针
    3. 类型检查后再访问:使用 type() 或 has_value()
    4. 重置空对象:使用 reset() 释放资源
    Logo

    openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

    更多推荐