STL里的容器要求只能是一种类型,比如
vector<int> v;
这个数组里就只能放int型数据,换了其它的数据,一不认识,二如果大了还放不下,当然,大多数情况不需要这样的功能,我要的就是一个int的数组,我疯了还往里面push一个浮点数?我也暂时没想到需要在一个容器里放多种类型数据的应用场合,至少这种要求也并不过分,就当是考验一下C++,它能做到吗?
boost::any库肯定的回答了这个问题,从名字上看,它能放下任何类型的数据,口气不小.可以这样定义:
using namespace boost;
vector<any> v;
v.push_back(any(100));
v.push_back(any(3.14));
boost的源码是免费的,下来打开看就知道了,整份源码只有200多行,很简洁很强大!它内嵌有一个基类placeholder,不做任何事,然后还有一个模板类holder<T>,它从placeholder派生下来,holder<T>是对T的一个封装,有一个成员就是T held,数据本身,然后在any类里面有一个placeholder的指针,基类指针可以指向任何子类成员,即holder<T>的对象,而holder<T>可以具现成任何类型T的一个holder - - (很简洁很强大吧)
画个图
placeholder指针->
任意类型的holder
holder<int>, holder<float> ...
这些都容易,但有一个问题,在需要一个容器放多种类型数据的时候,需要做一件事,运行时类型检查,vector的设计没有考虑这个,所以这件事自然落在any的身上,any也没什么怪招,实际上是C++的支持,对RTTI的支持.从any的对象中取数据,需要一个操作,any_cast,同时,它也完成了类型检查,any_cast是一个友元,传入一个any*,返回一个ValueType*,ValueType是一个模板参数,代码是这样的:
template<typename ValueType>
ValueType * any_cast(any * operand)
{
return operand && operand->type() == typeid(ValueType)
? &static_cast<any::holder<ValueType> *>(operand->content)->held
: 0;
}
typeid是C++的操作符,返回的是一个std::type_info&数据,传入一个类型名或者表达式,如果是两个相同类型则==操作符返回真,上面的代码就是,先做类型检查,如果类型相同,则返回数据的地址,否则返回0.
对了,any的构造函数采用new 操作,则所有数据都是从堆上分配,我想既然STL有专门优化过的allocator,为什么不用,而用原始的new?完全可以为any添加一个模板参数,指定分配器,这并不会使它看上去更复杂.
具体对any库的使用,尤其是any_cast的使用,见boost文档中的例程,不再话下.
附上刚才对typeid测试的程序,它可以做动态类型检测,只有当类中存在虚函数时,这似乎可以暗示我们,它是如何实现的.
#include <cstdlib>
#include <iostream>
using namespace std;
class Base
{
public:
virtual void foo(){}
};
class Derived : public Base
{
public:
void foo(){}
};
int main(int argc, char *argv[])
{
Base *pB=new Derived;
cout<<typeid(pB).name()<<endl
<<typeid(*pB).name()<<endl
<<(typeid(Base)==typeid(Derived))<<endl
<<typeid(Base).before(typeid(Derived))<<endl;
delete pB;
/*
pB=0;
cout<<typeid(*pB).name()<<endl; //在没有添加虚函之前,这段代码可以运行,因为它是静态的,在编译期就把*pB的类型确定了,实际上并没有执行对0指针的引用,添加之后,就是动态类型了,程序异常结束
*/
system("PAUSE");
return EXIT_SUCCESS;
}
rickone 2008/01/31
评论