Boost::any分析

来源:互联网 发布:base64 decode php 编辑:程序博客网 时间:2024/06/05 02:07

使用第三方库

以上介绍了Visual C++对对象赋值、转换及字符编码转换的方法,实际上还有一些好用的第三方类库用以辅助C++程序员完成对象处理,比较著名的就是boost。本节简单介绍boost库中与数值相关的boost::any、boost::lexical_cast,以及有理数类boost::rational。

4.6.1  万能类型boost::any

boost库提供了any类,boost::any是一个能保存任意类型值的类,这一点有点像variant类型,不过variant由于采用了一个巨大的union,效率非常低。而boost利用模板,保存的时候并不改变值的类型,只是在需要的时候才提供方法让用户进行类型判断及取值。

boost::any几乎可以用来存储任何数据类型:

[cpp] view plaincopyprint?
  1. boost::any ai, as;    
  2. ai = 100;    
  3. as = string( "hello" );   

需要的时候,我们又可以使用any_cast将原来的数据还原:

[cpp] view plaincopyprint?
  1. int  i = boost::any_cast< int >(ai);    
  2. string s = boost::any_cast<string>(as);   

当这种转换发生类型不匹配时,会有异常bad_any_cast发生:

[cpp] view plaincopyprint?
  1. try    
  2. {    
  3.    int  i = boost::any_cast< int >(as);    
  4. }    
  5. catch (boost::bad_any_cast & e)    
  6. {    
  7. }   

在传统的C++程序中,为了支持各种数据类型,我们不得不使用万能指针"void *",但是很遗憾的是,基于万能指针的转换是不安全的,"void*"缺少类型检查。所以,我们建议大家尽量使用any类。

现在动手

编写如下程序,体验如何使用boost::any来完成对象类型转换。

【程序 4-10】使用boost::any完成对象类型转换

[cpp] view plaincopyprint?
  1. #include "stdafx.h"   
  2. #include "boost/any.hpp"   
  3. #include <string>    
  4. using namespace std;    
  5. using namespace boost;    
  6. class Cat    
  7. {    
  8. };    
  9. void print(any it)    
  10. {    
  11.     if(it.empty())    
  12.     {    
  13.         printf("nothing!/r/n");    
  14.         return;    
  15.     }    
  16.     if(it.type() == typeid(int))    
  17.     {    
  18.         printf("integer: %d/r/n", any_cast<int>(it));    
  19.         return;    
  20.     }    
  21.     if(it.type() == typeid(string))    
  22.     {    
  23.         printf("string: %s/r/n", any_cast<string>(it).c_str());    
  24.         return;    
  25.     }    
  26.     if(it.type() == typeid(CString))    
  27.     {    
  28.         _tprintf(_T("CString: %s/r/n"), any_cast<CString>(it));    
  29.         return;    
  30.     }    
  31.     if(it.type() == typeid(Cat))    
  32.     {    
  33.         _tprintf(_T("oops! a cat!/r/n"));    
  34.         return;    
  35.     }    
  36. }    
  37. int main()    
  38. {    
  39.     print(100);    
  40.     any as[] = {any(), 100, string("hello"), CString("world"), Cat()};    
  41.     for(int i = 0; i < sizeof(as) / sizeof(as[0]); i++)    
  42.     {    
  43.         print(as[i]);    
  44.     }    
  45.     return 0;  
  46. }   

结果输出如图4-18所示。

 (点击查看大图)图4-18  运行结果

光盘导读

该项目对应于光盘中的目录"/ch04/BoostAnyTest"。

 

boost::any是一个能代表任何对象类型的对象,正如COM库的Variant变量类型,以及JAVA中的Object。不同的是,Variant的做法是包含所有可能类型的一个成员实现,浪费空间,而则boost::any借助于模板,没有空间浪费。

 

Variant的大致实现是:

Class Cvariant

{

intiData;

longlData;

….

inttype;

}


而boost::any则使用模板,依靠两个内部类来封装实际数据(PlaceFolder和Folder ),并对外暴露一个叫做Type()的函数暴露实际数据的类型。

为了方便分析其代码,现展示一个简单的测试代码:

<!-- @page { margin: 2cm } P { margin-bottom: 0.21cm } -->

#include"stdafx.h"

#include<iostream>

#include<list>

#include"boost/any.hpp"

 

typedefstd::list<boost::any> list_any;

//关键部分:可以存放任意类型的对象

voidfill_list(list_any& la)

{

//存放常数

la.push_back(10);

//存放字符串对象

la.push_back( std::string("dyunze") );

//注意la.push_back(“dyunze”)错误,因为会被当错字符串数组

}

//根据类型进行显示:

voidshow_list(list_any& la)

{

list_any::iterator it;

boost::any anyone;

for( it = la.begin(); it != la.end(); it++ )

{

anyone = *it;

if( anyone.type() ==typeid(int) )

std::cout<<boost::any_cast<int>(*it)<<std::endl;

elseif( anyone.type() ==typeid(std::string) )

std::cout<<boost::any_cast<std::string>(*it).c_str()<<std::endl;

}

}

//主程序部分:

intmain(intargc,char* argv[])

{

list_any la;

fill_list(la);

 

show_list(la);

return0;

}


以下是我整理了后的boost::any的关键代码,(只是为了说明,可能无法直接运行,如需要完整代码,请到www.boost.org下载boost库。 )如下所示:

  1. class any
  2. {
  3. public:
  4.     //模板构造函数,参数可以是任意类型,真正的数据保存在content中
  5.     template< typename ValueType>
  6.         any(const ValueType & value): content( new holder<ValueType>(value))
  7.     {
  8.     }
  9.     //析构函数,删除保存数据的content对象
  10.     ~any()
  11.     {
  12.         delete content;
  13.     }
  14.     //一个placeholde对象指针,只想其子类folder的一个实现
  15.     // 即content( new holder<ValueType>(value) )语句
  16.     placeholder * content;
  17. public:
  18.  
  19.     //查询真实数据的类型,拆葙时有用。
  20.     const std::type_info & type()  const
  21.     {
  22.         return content ? content->type() :  typeid( void);
  23.     }
  24.     /**一个稻草人,存在好处是没有模板参数,可以直接申明,
  25.      *如:       placeholder * content;
  26.      *如果使用子类folder类,则这能用older<Type>
  27.      *content,而申明时Type还不确定
  28.      */
  29.     class placeholder
  30.     {
  31.     public:   
  32.         virtual ~placeholder()
  33.         {
  34.         }
  35.     public:
  36.         virtual  const std::type_info & type()  const = 0;
  37.         virtual placeholder * clone()  const = 0;  
  38.     };
  39.     //真正保存和获取数据的类。
  40.     template< typename ValueType>
  41.         class holder :  public placeholder
  42.     {
  43.     public:
  44.         holder(const ValueType & value)
  45.             : held(value)
  46.         {
  47.         }
  48.     public:
  49.         virtual  const std::type_info & type()  const
  50.         {
  51.             return  typeid(ValueType);
  52.         }
  53.  
  54.         virtual placeholder * clone()  const
  55.         {
  56.             return  new holder(held);
  57.         }
  58.  
  59.     public:
  60.         //真正的数据,就保存在这里
  61.         ValueType held;
  62.     };
  63. };
  64. /**
  65.  *获取content->helder数据的方法。
  66.  *
  67.  */
  68.     template< typename ValueType>
  69. ValueType * any_cast(any * operand)
  70. {
  71.     return operand && operand->type() ==  typeid(ValueType) ? & static_cast<any::holder<ValueType> *>(operand->content)->held : 0;
  72. }


以上就是boost::any源代码的关键部分,其实很短小,但是,功能上非常强大,特别是在配合容器使用时。

 

 

万能转换器boost::lexical_cast

 

万能转换器boost::lexical_cast

boost::lexical_cast为数值之间的转换(conversion)提供了一揽子方案,比如:将一个字符串"123"转换成整数123,代码如下:

[cpp] view plaincopyprint?
  1. string s = "123";    
  2. int a = lexical_cast<int>(s);   

这种方法非常简单,笔者强烈建议大家忘掉std诸多的函数,直接使用boost:: lexical_cast。如果转换发生了意外,lexical_cast会抛出一个bad_lexical_cast异常,因此程序中需要对其进行捕捉。

现在动手

编写如下程序,体验如何使用boost:: lexical_cast完成数值转换。

【程序 4-11】使用boost:: lexical_cast完成对象数值转换

[cpp] view plaincopyprint?
  1. #include "stdafx.h"   
  2. #include <iostream>    
  3. #include <boost/lexical_cast.hpp>    
  4. using namespace std;    
  5. using namespace boost;    
  6. int main()    
  7. {    
  8.     string s = "123";    
  9.     int a = lexical_cast<int>(s);    
  10.     double b = lexical_cast<double>(s);    
  11.     printf("%d/r/n", a + 1);    
  12.     printf("%lf/r/n", b + 1);    
  13.     try   
  14.     {    
  15.         int c = lexical_cast<int>("wrong number");    
  16.     }    
  17.     catch(bad_lexical_cast & e)    
  18.     {    
  19.         printf("%s/r/n", e.what());    
  20.     }    
  21.     return 0;  
  22. }   

如上程序实现字符串"123"到整数、双精度实数的转换(为了防止程序作弊,我们特意让它将值加1),结果输出如图4-19所示。

 (点击查看大图)图4-19  运行结果

光盘导读

该项目对应于光盘中的目录"/ch04/LexicalCastTest"。

 

原创粉丝点击