C++项目:仿muduo库高并发服务器--------Any类的实现
文章目录
- 前言
- 一、服务器连接管理与通用协议上下文设计
- 二、Any类的设计思路
- 三、代码实现
前言
本篇文章是博主在学习仿muduo库高并发服务器项目所用到的功能模块,旨在设计一个可以存储任意类型的Any类。
一、服务器连接管理与通用协议上下文设计
在服务器的连接管理中,每个Connection对象负责一条客户端连接的管理,其必然涉及应用层协议的处理(如请求解析等)。因此,Connection内部必须设置协议处理上下文——用于记录协议解析进度、暂存中间数据(如:解析协议得到的数据),从而控制协议处理的节奏(例如应对网络通信中常见的粘包、半包问题)。
我们得到的数据包可能不是一个完整的请求,但是我们依然需要对数据包解析,这时就可以将本次解析得到的信息存储起来,直到得到一个完整的请求
而本项目需要满足不同应用层协议(如 HTTP、TCP 私有协议 等),若让协议上下文与特定协议强绑定,会大幅提升代码耦合度:后续新增或修改协议时,需频繁调整Connection的逻辑。为解决这一问题,协议的接收与解析上下文必须具有通用性,能够适配任意协议的上下文信息存储需求,这就需要一种可容纳不同数据结构的 “通用类型” 来承载。
从技术实现来看,不同语言 / 标准库提供了不同的通用类型解决方案:
- 在 C 语言中,通常通过void*(无类型指针) 实现通用数据管理,但这种方法不仅操作繁琐,还容易因类型不匹配引发安全问题;
- 在 C++ 中,可借助第三方库(如 Boost 库的boost::any),或使用 C++17 标准原生支持的std::any—— 二者均能安全存储任意类型的数据。
而考虑到本项目的移植性,这里我们自己实现轻量级的any类。
二、Any类的设计思路
需求:
- 一个连接需要有,请求接收与解析的上下文。
- 上下文类型不固定,服务器支持多种协议,不同协议上下文结构不同。
- 结论:得有能保存各种不同类型结构数据的容器。
- 任务:设计实现一个
any
类,且它是一个可以存储任意类型的容器。
这里可能多数人的第一反应是,使用类模板,但是这种方法在实例化对象时必须指定数据类型,实例后次类型就固定了,而这里我们需要的是可以存储任意类型的数据显然这是不合理的.
要满足“存储任意类型且无需在实例化时固定类型”的需求,可利用类继承 + 多态 + 类型擦除的思路设计 Any
类:
Any
类内部嵌套一个抽象基类(用于“类型擦除”,屏蔽具体类型差异),再通过模板子类承接具体数据的存储。借助多态特性,Any
能间接持有任意类型的对象,且无需在创建 Any
实例时预先指定存储类型。
结合代码理解
三、代码实现
为帮助大家理解如何达到消除类型的效果,下面代码借用AI注释
#include<iostream>
#include<cassert>
#include<typeinfo>
#include<string>
class Any{
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&operator=(const Any&other){Any(other).Swap(*this);return *this;}template<class T>//赋值重载Any&operator=(const T&val){Any(val).Swap(*this);return *this;}Any&Swap(Any&other){std::swap(this->_content,other._content);return *this;}template<class T>//返回子类对象,存储数据的指针T*get(){assert(typeid(T)==_content->type());return &(((placeholder<T>*)_content)->_val);}~Any(){delete _content;}private:
//holder 是一个纯抽象基类,它的关键特点是:完全不包含任何具体类型的信息,只定义了三个通用接口
//这个基类的作用是 “制定统一标准”:无论存储的具体类型是什么(int/string/ 自定义类型)
//对外都必须通过这三个接口交互。从这一层开始,“具体类型” 的细节就被隐藏了。class holder{public:virtual ~holder(){}virtual holder*clone()=0;virtual const std::type_info& type()=0;//查看存储类型};
//placeholder<T> 是模板类,它继承自 holder,但与具体类型 T 绑定
//向下:直接操作具体类型 T(存储 _val、拷贝 _val);
//向上:通过重写 holder 的接口,将 T 的细节 “包装” 成通用接口。template<class T>class placeholder:public holder{public:placeholder(const T&val):_val(val){}virtual holder* clone(){return new placeholder(_val);}//根据自己克隆一个新的对象virtual const std::type_info& type(){return typeid(T);}//获取存储对象类型T _val;};holder* _content;
};
//Any 类是最终对外的 “容器”,它的核心成员是 holder* _content,这个指针指向 placeholder<T> 实例,但 Any 类本身 完全不知道 T 是什么
//存储时:无论传入 int、string 还是自定义类型,都被 placeholder<T> 封装成 holder*,Any 无需关心 T;
//传递时:Any 对象的拷贝 / 赋值(如 a = b)通过 holder 的 clone() 接口完成,与具体类型无关;
//取值时:才通过 type() 接口校验类型,再转换回 T*—— 但这个 “类型恢复” 是使用者主动指定 T 的结果,Any 本身仍不感知 T。//测试
// class Test{
// public:
// Test()
// {
// std::cout<<"构造"<<std::endl;
// }
// Test(const Test&t)
// {
// std::cout<<"拷贝"<<std::endl;
// }
// ~Test()
// {
// std::cout<<"析构"<<std::endl;
// }
// };
// int main()
// {
// Any a;
// a=10;
// int *p=a.get<int>();
// std::cout<<*p<<std::endl;
// a=std::string("aaaa");
// std::string*sp=a.get<std::string>();
// std::cout<<*sp<<std::endl;// Test t;
// a=t;// return 0;
// }