正文

boost::any实现一个容器里放多种类型的对象2008-01-31 23:44:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/rickone/32506.html

分享到:

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

阅读(5543) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册