使用第三方库
以上介绍了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几乎可以用来存储任何数据类型:
- boost::any ai, as;
- ai = 100;
- as = string( "hello" );
需要的时候,我们又可以使用any_cast将原来的数据还原:
- int i = boost::any_cast< int >(ai);
- string s = boost::any_cast<string>(as);
当这种转换发生类型不匹配时,会有异常bad_any_cast发生:
- try
- {
- int i = boost::any_cast< int >(as);
- }
- catch (boost::bad_any_cast & e)
- {
- }
在传统的C++程序中,为了支持各种数据类型,我们不得不使用万能指针"void *",但是很遗憾的是,基于万能指针的转换是不安全的,"void*"缺少类型检查。所以,我们建议大家尽量使用any类。
现在动手
编写如下程序,体验如何使用boost::any来完成对象类型转换。
【程序 4-10】使用boost::any完成对象类型转换
- #include "stdafx.h"
- #include "boost/any.hpp"
- #include <string>
- using namespace std;
- using namespace boost;
- class Cat
- {
- };
- void print(any it)
- {
- if(it.empty())
- {
- printf("nothing!/r/n");
- return;
- }
- if(it.type() == typeid(int))
- {
- printf("integer: %d/r/n", any_cast<int>(it));
- return;
- }
- if(it.type() == typeid(string))
- {
- printf("string: %s/r/n", any_cast<string>(it).c_str());
- return;
- }
- if(it.type() == typeid(CString))
- {
- _tprintf(_T("CString: %s/r/n"), any_cast<CString>(it));
- return;
- }
- if(it.type() == typeid(Cat))
- {
- _tprintf(_T("oops! a cat!/r/n"));
- return;
- }
- }
- int main()
- {
- print(100);
- any as[] = {any(), 100, string("hello"), CString("world"), Cat()};
- for(int i = 0; i < sizeof(as) / sizeof(as[0]); i++)
- {
- print(as[i]);
- }
- return 0;
- }
结果输出如图4-18所示。
(点击查看大图)图4-18 运行结果 |
光盘导读
该项目对应于光盘中的目录"/ch04/BoostAnyTest"。
boost::any是一个能代表任何对象类型的对象,正如COM库的Variant变量类型,以及JAVA中的Object。不同的是,Variant的做法是包含所有可能类型的一个成员实现,浪费空间,而则boost::any借助于模板,没有空间浪费。
Variant的大致实现是:
Class Cvariant
{
int iData;
long lData;
….
int type;
}
而boost::any则使用模板,依靠两个内部类来封装实际数据(PlaceFolder和Folder ),并对外暴露一个叫做Type()的函数暴露实际数据的类型。
为了方便分析其代码,现展示一个简单的测试代码:
<!-- @page { margin: 2cm } P { margin-bottom: 0.21cm } -->
#include "stdafx.h"
#include <iostream>
#include <list>
#include "boost/any.hpp"
typedef std::list<boost::any> list_any;
// 关键部分:可以存放任意类型的对象
void fill_list(list_any& la)
{
// 存放常数
la.push_back(10);
// 存放字符串对象
la.push_back( std::string( "dyunze" ) );
// 注意 la.push_back(“dyunze”) 错误,因为会被当错字符串数组
}
// 根据类型进行显示:
void show_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;
else if ( anyone.type() == typeid (std::string) )
std::cout<<boost::any_cast<std::string>(*it).c_str()<<std::endl;
}
}
// 主程序部分:
int main( int argc, char * argv[])
{
list_any la;
fill_list(la);
show_list(la);
return 0;
}
以下是我整理了后的boost::any的关键代码,( 只是为了说明,可能无法直接运行,如需要完整代码,请到www.boost.org下载boost库。 )如下所示:
- class any
- {
- public :
- //模板构造函数,参数可以是任意类型,真正的数据保存在content中
- template < typename ValueType>
- any(const ValueType & value): content( new holder<ValueType>(value))
- {
- }
- //析构函数,删除保存数据的content对象
- ~any()
- {
- delete content;
- }
- //一个placeholde对象指针,只想其子类folder的一个实现
- // 即content( new holder<ValueType>(value) )语句
- placeholder * content;
- public :
- //查询真实数据的类型,拆葙时有用。
- const std::type_info & type() const
- {
- return content ? content->type() : typeid ( void );
- }
- /**一个稻草人,存在好处是没有模板参数,可以直接申明,
- *如: placeholder * content;
- *如果使用子类folder类,则这能用older<Type>
- *content,而申明时Type还不确定
- */
- class placeholder
- {
- public :
- virtual ~placeholder()
- {
- }
- public :
- virtual const std::type_info & type() const = 0;
- virtual placeholder * clone() const = 0;
- };
- //真正保存和获取数据的类。
- template < typename ValueType>
- class holder : public placeholder
- {
- public :
- holder(const ValueType & value)
- : held(value)
- {
- }
- public :
- virtual const std::type_info & type() const
- {
- return typeid (ValueType);
- }
- virtual placeholder * clone() const
- {
- return new holder(held);
- }
- public :
- //真正的数据,就保存在这里
- ValueType held;
- };
- };
- /**
- *获取content->helder数据的方法。
- *
- */
- template < typename ValueType>
- ValueType * any_cast(any * operand)
- {
- return operand && operand->type() == typeid (ValueType) ? & static_cast <any::holder<ValueType> *>(operand->content)->held : 0;
- }
以上就是boost::any源代码的关键部分,其实很短小,但是,功能上非常强大,特别是在配合容器使用时。
万能转换器boost::lexical_cast
boost::lexical_cast为数值之间的转换(conversion)提供了一揽子方案,比如:将一个字符串"123"转换成整数123,代码如下:
- string s = "123";
- int a = lexical_cast<int>(s);
这种方法非常简单,笔者强烈建议大家忘掉std诸多的函数,直接使用boost:: lexical_cast。如果转换发生了意外,lexical_cast会抛出一个bad_lexical_cast异常,因此程序中需要对其进行捕捉。
现在动手
编写如下程序,体验如何使用boost:: lexical_cast完成数值转换。
【程序 4-11】使用boost:: lexical_cast完成对象数值转换
- #include "stdafx.h"
- #include <iostream>
- #include <boost/lexical_cast.hpp>
- using namespace std;
- using namespace boost;
- int main()
- {
- string s = "123";
- int a = lexical_cast<int>(s);
- double b = lexical_cast<double>(s);
- printf("%d/r/n", a + 1);
- printf("%lf/r/n", b + 1);
- try
- {
- int c = lexical_cast<int>("wrong number");
- }
- catch(bad_lexical_cast & e)
- {
- printf("%s/r/n", e.what());
- }
- return 0;
- }
如上程序实现字符串"123"到整数、双精度实数的转换(为了防止程序作弊,我们特意让它将值加1),结果输出如图4-19所示。
(点击查看大图)图4-19 运行结果 |
光盘导读
该项目对应于光盘中的目录"/ch04/LexicalCastTest"。