关闭

C++ 有名库的介绍, 准标准库Boost

924人阅读 评论(0) 收藏 举报

在C++中,库的地位是非常高的。C++之父 Bjarne Stroustrup先生多次表示了设计库来扩充功能要好过设计更多的语法的言论。现实中,C++的库门类繁多,解决的问题也是极其广泛,库从轻量级到重量级的都有。不少都是让人眼界大开,亦或是望而生叹的思维杰作。由于库的数量非常庞大,而且限于笔者水平,其中很多并不了解。所以文中所提的一些库都是比较著名的大型库。

Boost库是一个经过千锤百炼、可移植、提供源代码的C++库,作为标准库的后备,是C++标准化进程的发动机之一。 Boost库由C++标准委员会库工作组成员发起,在C++社区中影响甚大,其成员已近2000人。 Boost库为我们带来了最新、最酷、最实用的技术,是不折不扣的“准”标准库。

Boost中比较有名气的有这么几个库:
Regex
正则表达式库
Spirit
LL parser framework,用C++代码直接表达EBNF
Graph
图组件和算法
Lambda
在调用的地方定义短小匿名的函数对象,很实用的functional功能
concept check
检查泛型编程中的concept
Mpl
用模板实现的元编程框架
Thread
可移植的C++多线程库
Python
把C++类和函数映射到Python之中
Pool
内存池管理
smart_ptr
5个智能指针,学习智能指针必读,一份不错的参考是来自CUJ的文章:
Smart Pointers in Boost,哦,这篇文章可以查到,CUJ是提供在线浏览的。中文版见笔者在《Dr.Dobb's Journal软件研发杂志》第7辑上的译文。

Boost总体来说是实用价值很高,质量很高的库。并且由于其对跨平台的强调,对标准C++的强调,是编写平台无关,现代C++的开发者必备的工具。但是Boost中也有很多是实验性质的东西,在实际的开发中实用需要谨慎。并且很多Boost中的库功能堪称对语言功能的扩展,其构造用尽精巧的手法,不要贸然的花费时间研读。Boost另外一面,比如Graph这样的库则是具有工业强度,结构良好,非常值得研读的精品代码,并且也可以放心的在产品代码中多多利用。

lexical_cast
这次我们先挑个简单实用的Boost组件,看看Boost能给我们带来怎样的便利。

字符串→数值
在CSDN论坛上经常看到询问如何在字符串类型和数值类型间进行转换的问题,也看到了许多不同的答案。下面先讨论一下从字符串类型到数值类型的转换。

如何将字符串"123"转换为int类型整数123?答案是,用标准C的库函数atoi;
如果要转换为long类型呢?标准C的库函数atol;
如何将"123.12"转换为double类型呢?标准C的库函数atod;
如果要转换为long double类型呢?标准C的库函数atold;
……

后来有朋友开始使用标准库中的string类,问这个如何转换为数值?有朋友答曰,请先转换为const char*。我很佩服作答者有数学家的思维:把陌生的问题转化成熟悉的问题。(曾经有一则笑话,好事者问数学家:知道如何烧水吗?答:知道。把水壶加满水,点火烧。又问:如果水壶里已经有水了呢?答:先倒掉,就转化为我熟悉的问题了……)

不,不,这样是C的做法,不是C++。那么,C++该怎么做呢?使用Boost Conversion Library所提供的函数lexical_cast(需要引入头文件boost/lexical_cast.hpp)无疑是最简单方便的。如:

#include
#include
int main()
{
using boost::lexical_cast;
int a = lexical_cast("123");
double b = lexical_cast("123.12");
std::cout<<
<
std::cout<<
<
return 0;
}
一个函数就简洁地解决了所有的问题。

数值→字符串
那么从数值类型到字符串类型呢?

用 itoa?不对吧,标准C/C++里根本没有这个函数。即使在Windows平台下某些编译器提供了该函数[3],没有任何移植性不说,还只能解决int 类型(也许其他函数还可以解决long、unsigned long等类型),浮点类型又怎么办?当然,办法还是有,那就是:sprintf。

char s[100];
sprintf(s, "%f", 123.123456);

不知道诸位对C里的scanf/printf系列印象如何,总之阿炯我肯定记不住那些稀奇古怪的参数,而且如果写错了参数,就会得到莫名其妙的输出结果,调试起来可就要命了(我更讨厌的是字符数组,空间开100呢,又怕太小装不下;开100000呢,总觉得太浪费,心里憋气,好在C++标准为我们提供了 string这样的字符串类)。这时候,lexical_cast就出来帮忙啦。
#include
#include
#include
int main()
{
using std::string;
const double d = 123.12;
string s = boost::lexical_cast(d);
std::cout<<
<
return 0;
}
跟前面一样简单。

异常
如果转换失败,则会有异常bad_lexical_cast抛出。该异常类是标准异常类bad_cast的子类。

#include
#include
int main()
{
using std::cout;
using std::endl;
int i;
try{
i = boost::lexical_cast("abcd");
}
catch(boost::bad_lexical_cast& e)
{
cout<<
return 1;
}
cout<<
return 0;
}
显然“abcd”并不能转换为一个int类型的数值,于是抛出异常,捕捉后输出“bad lexical cast: source type value could not be interpreted as target”这样的信息。

注意事项
lexical_cast依赖于字符流std::stringstream(会自动引入头文件[4]),其原理相当简单:把源类型读入到字符流中,再写到目标类型中,就大功告成。例如

int d = boost::lexical_cast("123");
就相当于
int d;
std::stringstream s;
s<<"123";
s>>d;
既然是使用了字符流,当然就有些随之而来的问题,需要特别指出[5]。
由于Visual C++ 6的本地化(locale)部分实现有问题,因此如果使用了非默认的locale,可能会莫名其妙地抛出异常。当然,一般情况下我们并不需要去改变默认的locale,所以问题不是很大。

输入数据必须“完整”地转换,否则抛出bad_lexical_cast异常。例如
int i = boost::lexical_cast("123.123"); // this will throw
便会抛出异常。因为“123.123”只能“部分”地转换为123,不能“完整”地转换为123.123。

浮点数的精度问题。
std::string s = boost::lexical_cast(123.1234567);
以上语句预想的结果是得到“123.1234567”,但是实际上我们只会得到“123.123”,因为默认情况下std::stringstream的精度是6(这是C语言程序库中的“前辈”printf留下的传统)。这可以说是boost::lexical_cast的一个bug。怎么办呢?权宜之计,可以这么做:打开头文件,注意对照修改[6]:
#include
//...
template
Target lexical_cast(Source arg) {
//...
Target result;
interpreter.precision(std::numeric_limits::digits10);
if( !(interpreter << arg) ||
!(interpreter >> result) ||
!(interpreter >> std::ws).eof())
//...
}
即可得到正确结果。当然,理论上效率会有一点点损失,不过几乎可以忽略不计。

小结
我们已经体验了boost::lexcial_cast。当然,lexical_cast不仅仅局限于字符串类型与数值类型之间的转换:可在任意可输出到 stringstream的类型和任意可从stringstream输入的类型间转换。这次的了解尽管很粗略,不过毕竟我们已经“走进Boost”,而不仅仅是“走近”。以后,我们可以自行领略Boost的动人之处啦。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:88843次
    • 积分:1202
    • 等级:
    • 排名:千里之外
    • 原创:15篇
    • 转载:58篇
    • 译文:0篇
    • 评论:36条
    文章分类
    最新评论