问题:
对于这样的场景:对于一些CS(客户端-服务端)模型,当用户在客户端传入相应的事件时,我们需要实现框架即在服务端去分配线程处理这些事件,即调用用户的事件处理函数,那么对于不同的用户的事件函数,如何统一在服务端进行处理呢,那么容易想到的一个方法就是多态,如下:
class Task // 服务端实现的
{
public:Task();~Task() = default;// 用户可以自定义任意任务类型,从Task继承,重写run方法,实现自定义任务处理virtual Any run() = 0;};
class MyTask : public Task //用户端想要处理的事件
{
public:MyTask(int begin, int end){}Any run() {... //用户的事件return sum; //处理的结果返回}};
此时服务端,就可以调用派生类的run方法实现用户的事件了,此时问题就出现了,那么用户如何获取事件处理的返回结果呢,要知道多态重写的是同名函数,包括返回值,所以我们需要一个统一的Any类型接收任意类型的参数返回,如int、string、vector等,如上所示
ps:模板和继承无法共存,也就是不能给run方法添加模返回参数,只能使用自定义的Any类型接收所有的返回类型。
简要来说,我需要一个统一类型接收所有类型,并且统一类型可以推断接收的所有类型它到底是什么类型
Any的设计思路
1:首先要接收任意的返回类型:即联想到模板
2:Any拥有可以一个统一的类型指向任意类型,即联想到继承
Any的实现
首先,Any类型接收一个任意类型的参数,也就是对其构造函数,模板化,同时拥有了一个成员指针,它是一个统一的指针,也就是接下来我会将它设计会基类的指针,以后会指向继承于它的派生类对象,注意我使用的是unique_ptr,所以手动声明了右值引用的拷贝和赋值,而删除了左值引用的拷贝和赋值(原因自查unique_ptr)
class Any
{
public:Any() = default;~Any() = default;Any(const Any&) = delete;Any& operator =(const Any&) = delete;Any(Any&&) = default;Any& operator=(Any&&) = default;template<typename T>Any(T data) {}
private:std::unique_ptr<Base> base_;
};
注意到,T data其实就是上文的 int sum,那么如何让base指针统一指向这些不同的对象呢,就是把base作为基类指针,构造初始化的时候,将data去初始化一个继承于base的派生类对象,然后让base指向这个派生类对象,就完成了统一类型指向任意类型了。也就是在下面,我继续实现了继承的关系方法。
class Any
{
public:Any() = default;~Any() = default;Any(const Any&) = delete;Any& operator =(const Any&) = delete;Any(Any&&) = default;Any& operator=(Any&&) = default;template<typename T>Any(T data) : base_(std::make_unique<Derive<T>>(data)){} private:class Base{public:virtual ~Base() = default;};//template<typename T>class Derive : public Base {public:Derive(T data): data_(data){}T data_;};
private:std::unique_ptr<Base> base_;
};
最后,用户最后得到的Any对象:Any res = ...,这是一个任意对象,如何变为用户的事件处理的结果的类型呢,就需要进行转换,因为T data就表示int sum,那么就想到我们需要从base里面找到我指向的派生类对象,所以利用类型的转换,这里是基类向派生类转换,所以用到了dynamic_cast的安全转换,然后取出Derive_对象的成员data_就完成了,整体如下:
#include<iostream>
#include <memory>class Any
{
public:Any() = default;~Any() = default;Any(const Any&) = delete;Any& operator =(const Any&) = delete;Any(Any&&) = default;Any& operator=(Any&&) = default;template<typename T>Any(T data) : base_(std::make_unique<Derive<T>>(data)){} template<typename T>T cast(){Derive<T> *pd = dynamic_cast<Derive<T>*> (base_.get());if (pd == nullptr){std::cout << "type unmatch" << std::endl;}return pd->data_;}private:class Base{public:virtual ~Base() = default;};//template<typename T>class Derive : public Base {public:Derive(T data): data_(data){}T data_;};
private:std::unique_ptr<Base> base_;
};