前言
boost::any类为我们提供了一个十分强大的功能:只要定义一个any对象,就可以保存任意类型的数据到这个对象中,而且还可以动态改变类型。这比我么在COM中使用到的VARIANT结构要强大多了,VARIANT只不过是用到了一个联合体,把可能出现的类型全部包括进去了,更像是一种穷举,而且还有一个标识来说明当前结构中保存的数据的类型。
如何使用any?
去boost官网下载对应的ZIP包,解压后运行那个exe编译完成后,把对应的头文件和库文件路径添加到VS的路径里面去。
然后包括下这个头文件即可
#include <boost/any.hpp>
简单的测试代码
<span style="white-space:pre"> </span>boost::any a1 = 123; if ( a1.empty() ) cout<<"a1容器不为空"<<endl; else cout<<"a1容器为空"<<endl; a1 = string("string::123"); a1 = 12.398; if ( !a1.empty() ) { cout<<"now a1 = "<<a1<<", a1.type = "<<typeid(a1).name()<<endl; }
直接这样编译是会报错的,在C++中除了标准库定义的类外,我们自己定义的类使用<<\>>标准输入输出流时需要自己来重载<<操作符的。至于重载函数的写法,基本上都是这样的:作为类的友元函数,传入一个ostream对象的引用,输出后返回这个引用。
ostream& operator <<( ostream& out, const boost::any& a ) { if ( a.type() == typeid(int) ) out<<boost::any_cast<int>(a); else if ( a.type() == typeid(string) ) out<<boost::any_cast<string>(a); else if ( a.type() == typeid(double) ) out<<boost::any_cast<double>(a); else if ( a.type() == typeid(float) ) out<<boost::any_cast<float>(a); //这里如果未把any对应的类型进行处理将直接跳过,不能输出任何信息 return out; }
因为我们不知道any的类型,需要借助any_cast来转换,查看any_cast的源码
template<typename ValueType> ValueType * any_cast(any * operand) { return operand && #ifdef BOOST_AUX_ANY_TYPE_ID_NAME std::strcmp(operand->type().name(), typeid(ValueType).name()) == 0 #else operand->type() == typeid(ValueType) #endif ? &static_cast<any::holder<ValueType> *>(operand->content)->held : 0; }
代码的意思:如果any指针不为空,而且它的类型和我们传入的类型一致,那么返回any中保存的那个数据的地址;否则,返回空指针。
any=内部如何实现的呢?
看了下源代码,依葫芦画瓢写了个差不多的类
class MyAny { public: //默认构造函数 MyAny() :m_pValue(NULL) { } template<class T> MyAny(const T& t) :m_pValue(new CValue<T>(t)) { } //拷贝构造函数 MyAny(MyAny& ma) :m_pValue(ma.IsEmpty()?NULL:ma.m_pValue->Clone()) { } virtual ~MyAny() { if ( m_pValue ) { delete m_pValue; } } MyAny& Swap(MyAny& ma) { //交换两个对象的地址,避免了重新申请释放内存,提高效率 std::swap( ma.m_pValue, m_pValue); return *this; } template<class T> MyAny& operator=(const T& val) { //重载=操作符,先是构造一个MyAny对象,Awap交换对象指针后,原来需要释放的那个指针就到了临时对象MyAny(val)中 //函数返回后,临时变量自动析构,调用了析构函数释放这块内存,不会造成内存泄露。 MyAny(val).Swap(*this); return *this; } bool IsEmpty() { return !m_pValue; } protected: //基类声明 class IValueBase { public: virtual const char* GetTypeName() = 0; virtual IValueBase* Clone() = 0; }; //子类扩展为模板类 template<class TypeName> class CValue : public IValueBase { public: CValue(const TypeName& val) :_value(val) { } virtual const char* GetTypeName() { return typeid(TypeName).name(); } virtual IValueBase* Clone() { //拷贝一份数据 return new CValue<TypeName>(_value); } private: //模板类对象中存储着any数据 TypeName _value; }; private: //虚基类的指针,指向一个派生的模板类对象 IValueBase* m_pValue; //重载操作符,用于C++的格式化输出,定义为友元函数 friend ostream& operator<<(ostream& out, const MyAny& ma); };
//重载函数,用于标准输出
ostream& operator<<(ostream& out, const MyAny& ma)
{
out<<ma.m_pValue->GetTypeName();
return out;
}
any中保存着一个基类的指针,创建的时候其实是指向了他的派生类对象,派生类是模板类。
真正的数据保存和交换都是在派生类CValue中的。any会根据构造函数或者赋值函数传入的数据类型创建一个IValueBase指针来存放这个数据,然后智能地释放掉上次那个数据的空间,详细看代码注释。