zoukankan      html  css  js  c++  java
  • c++任意类型Any类的实现

      在java或c#中,Object类型对象可以指向任意类型的变量,因为所有的类默认都从Object类继承。但是在c++中,没有类似Object类这样的类型,而很多时候,为了设计出通用的程序,往往需要类似于Object类型作为参数或者返回值。例如,在另一篇文章《c++实现反射类》中就用到了可以指向任意类型的Any类。

      在c或者c++中,可以指向任意类型的关键字就是无符号类型void*,任何一个对象都可以使用void*来指向。例如以下类:
    class A {
     privateint a;
    };
    
    class B {
     privatechar b;
    };
     
    int i = 10;
    A a;
    B b;
    void* tmp = &i;
    tmp = &a;
    tmp = &b;
    上述程序不会有任何问题,如果是这样,是否还需要Any类型呢?void*是否可以解决一切问题?我们继续往下看,
    void* any = &a;
    B* b = (B*)any;
    这里,首先将类型A的变量赋值给any,然后又将any赋值给类型B的变量,而且毫无问题,编译器不会给出任何警告,如果你非常不幸的话,程序毫无问题的运行,直到某一天在关键时刻整个系统崩溃。看来,void*实在是太万能,以至于对它执行任意操作都不会有问题。很显然,这种使用方式极其不安全。另外,因为void*是指针,所指向的对象如果已经被释放,再使用any时就会出现问题,这种情况下,需要重新new一个相同的对象,使用any指向new的对象,不过这样的话需要自己管理指针,使用起来会非常麻烦。因此,我们另想办法来实现Any类型而不使用void*。
      首先,如果可以接受任意类型的数据,Any类首要的一个特性就是与类型无关。在c++中,想做到类型无关,第一选择必是使用模板。而且我们希望这样使用Any类型:
    Any any(string("test"));
    Any any(10);
    A a;
    Any any = a;
    上述使用方式中,要求在定义Any类型时,不会显式指定Any类型,这样的话,Any就不能定义成一个模板类,因此符合上述使用要求的Any类的定义大致如下:
    template<typename ValueType>
    Any(ValueType value) {
        content_ = value;  // content_是Any类成员,保存数据副本
    }
      template<typename ValueType>
      Any& operator=(ValueType value) {
        Any(value).Swap(*this);
        return *this;
      }
    需要注意的是,=的重载为什么不使用content_ = value,有两个原因:
    1. 如果ValueType是Any类型,会陷入函数调用无穷递归;
    2. 如果content_里面包含需要释放的资源,直接赋值的话,之前的资源不会释放;
    因此,写成如上形式,使用一个临时对象保存参数值,在执行完Swap语句后,临时对象会被销毁,参数值通过临时对象交换到this指针指向的对象,this指向对象被交换到临时对象中,随着临时对象的销毁,this指向对象之前拥有的资源也会被回收或者释放。使用这种方式很方便地完成赋值操作。
    有了以上两个函数,就可以这样使用:
    A a;
    Any any(a);
    Any any(10);
    Any any = new A;
    
    Any any;
    any = a;
      这样,通过使用函数模板,Any类型可以接受任意类型的变量。到这里,又有另外一个问题,Any中的content_应该定义成什么类型。在我们看来,content_同样可以接受任意类型变量,那么content_也应该定义成Any类型,但是这样会陷入定义无穷递归。如何避免这种情况在c++中经常遇到,就是定义content_为Any类型指针。但是单纯一个指针无法保存指向对象的数据,因此,需要再新建一个类似Any类型的类,这个类专门负责保存Any类型指向对象的数据副本。因为需要保存任意类型的数据,可以将其定义为模板类:
    template<typename ValueType>
    class Holder {
     private:
      ValueType held_;
    };
    类Holder可以保存任意类型的数据,不过在使用Holder类时,需要显示指定模板参数,如下:
    holder<int> i_holder;
    holder<string> str_holder;
    然而Any类中content_变量在定义时没有显示指定类型,因为Any不是模板类,没有模板类型参数传递给content_。为了解决这种情况,再定义一个Holder的基类,如下:
    class PlaceHolder {
    public:
      virtual ~PlaceHoder() {}
    };
    
    template<typename ValueType>
    class Holder : public PlaceHolder {
    public:
    Holder(const ValueType& value) : held(value) {}
    private: ValueType held_; };
    定义完Holder基类PlaceHolder,就可以将Any类中的content_定义成PlaceHolder类指针类型了,现在,Any类基本框架已经搭建完,目前Any类如下:
    class Any {
     public:
      Any() : content_(NULL) {}
      template<typename ValueType>
      Any(const ValueType& value) : content_(new Holder<ValueType>(value)) {
      }
      ~Any() {
        delete content_;
      }
    
     private:
      PlaceHolder* content_;
    };
    但是,这里还有一个问题(谢谢壮壮熊的提示),那就是如果这样使用:
    Any a(1);
    Any b(a);

    程序就会挂掉,因为b变量保存的是Any类型,即content_是Any类型,b在调用析构函数时调用delete content_语句,该语句又会调用content_的析构函数,因为这里的content_是Any类型,所以Any析构函数就陷入无穷递归调用。因此,这里,需要定义Any另外一个接受Any类型参数的构造函数:

    Any(const Any& other) : content_(other.content_ ? other.content_->clone() : NULL) {}

    // Holder中的clone函数
    virtual PlaceHoder* clone() const {
    return new Holder(held_);
    }
    然而,毕竟Any类型只是作为中间媒介来保存和传递数据,最终还是需要将Any转换成相应类型的对象,因此必须定义如下函数:
    template<typename ValueType>
    ValueType* any_cast() {
      if (content == NULL)
        return NULL;
      else
        return static_cast<holder<ValueType> *>(content_)->held_;
    }
    到这里,Any类基本功能就已经具备了。但是,这时的Any类也存在上面void*提到的问题,即没有类型检查,可以将Any类型转换成任意类型。在c++中,有个高级的功能就是运行时类型识别(RTTI),其中可以使用typeid操作符获得指针或引用所指对象的实际类型,因此,在进行类型转换时可以比较Any中存储的类型是否与转换的类型符合,如果不符合则转换失败打印日志,如果符合则转换成功,这里可以根据具体应用来控制转换结果。
     
    本文中需要注意学习的知识点:
    1. 模板编程;
    2. =重载实现;
    3. 模板类继承;
    4. 临时对象的使用;
    5. 类型识别以及类型转换;
    注:
    本文的Any类借鉴boost中Any的实现,对其实现过程进行了剖析,文中程序只是样例,如果使用的话请直接使用boost中的Any类。
  • 相关阅读:
    Asp.net core 项目的发布和运行环境部署完整介绍(Window+IIS7)
    让IIS支持PUT和Delete请求
    js实现图片打点
    Ogre空间变换,setPosition(),setDerivedPosition()和translate()的正确使用。
    obs 工作室模式
    obs局域网串流插件之“OBSNDI”
    obs 媒体源获取之“VLC视频源”
    谈谈php中的mvc
    PHP 开发之MVC架构
    一个简单的php MVC实例
  • 原文地址:https://www.cnblogs.com/feixue/p/boost-any.html
Copyright © 2011-2022 走看看