在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
在java或c#中,Object类型对象可以指向任意类型的变量,因为所有的类默认都从Object类继承。但是在c++中,没有类似Object类这样的类型,而很多时候,为了设计出通用的程序,往往需要类似于Object类型作为参数或者返回值。例如,在另一篇文章《c++实现反射类》中就用到了可以指向任意类型的Any类。 在c或者c++中,可以指向任意类型的关键字就是无符号类型void*,任何一个对象都可以使用void*来指向。例如以下类:
class A { private: int a; }; class B { private: char 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,有两个原因:
因此,写成如上形式,使用一个临时对象保存参数值,在执行完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 {
定义完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) {}
然而,毕竟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中存储的类型是否与转换的类型符合,如果不符合则转换失败打印日志,如果符合则转换成功,这里可以根据具体应用来控制转换结果。
本文中需要注意学习的知识点:
注:
本文的Any类借鉴boost中Any的实现,对其实现过程进行了剖析,文中程序只是样例,如果使用的话请直接使用boost中的Any类。
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论