前言
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指针来存放这个数据,然后智能地释放掉上次那个数据的空间,详细看代码注释。