1、BOOST 应用初探作者:冯刚2011-8-41 背景BOOST 是由 c+标准化委员会成员参与开发的 c+标准的准官方库,他是 C+标准库的发动机。下一代 C+标准 C+0x 的标准库很多来自于 boost。Boost 库覆盖了广泛的领域,从数学库到智能指针,从模板元编程库到预处理器库及语法词法分析,从线程到 lambda 表达式,等等。所有 Boost 库都具有宽松的许可证,确保库可以被自由使用于商用软件。2 功能2.1概况Boost 库涵盖了很广泛的领域,下面就常用的功能像智能指针,正则表达式,函数对象bind 和 function, 线程和线程池,时间日期,多索引容器,哈希容器,bim
2、ap,日志,内存池,模板元编程,循环缓冲区,tuple, 观察者模式 signal2,网络通讯 asio 做一些介绍。2.2功能2.2.1 智能指针2.2.1.1 概论smart pointers(智能指针)是存储“指向动态分配(在堆上)的对象的指针”的对象。他们的行为很像 C+ 的内建指针,只是它们可以在适当的时候自动删除它们所指向的对象。智能指针在面对异常时有非常显著的作用,它们可以确保动态分配对象的完全析构。它们还可以用于跟踪多主人共享的动态分配对象。在概念上,智能指针可以看作拥有它所指向的对象,并因此在对象不再需要时负责将它删除。智能指针库提供了六个智能指针类模板:scoped_ptr
3、 简单的单一对象的唯一所有权。不可拷贝。scoped_array 简单的数组的唯一所有权。不可拷贝。shared_ptr 在多个指针间共享的对象所有权。shared_array 在多个指针间共享的数组所有权。weak_ptr 一个属于 shared_ptr 的对象的无所有权的观察者。intrusive_ptr 带有一个侵入式引用计数的对象的共享所有权。这些模板被设计用来补充 std:auto_ptr 模板的不足。2.2.1.2 智能指针 shared_ptrshared_ptr 类模板存储一个指向动态分配对象(一般是用 C+ new-expression 生成的)的指针。在最后一个 share
4、d_ptr 所指向的对象被销毁或重置时,要保证它所指向的对象被删除。每一个 shared_ptr 都符合 C+ 标准库的 CopyConstructible 和 Assignable 的必要条件,并因此能够用于标准库容器。因为提供了比较操作,因此 shared_ptr 可以和标准库中的关联式容器一起工作。通常,一个 shared_ptr 不能正确地持有一个指向动态分配的数组的指针。关于那种用法请参见 shared_array。因为在实现中使用了引用计数,shared_ptr 实例的循环引用不会被回收。例如,如果 main() 持有一个指向 A 的 shared_ptr, A 又直接或间接持有一
5、个指回 A 的 shared_ptr,A 的使用计数是 2。最初的 shared_ptr 析构后将导致一个使用计数为 1 的 A 被悬挂。使用 weak_ptr 以“打破循环”。这个类模板被 T 参数化,T 是被指向的对象的类型。shared_ptr 和它的大多数成员函数对于 T 没什么要求,允许它是一个不完整类型,或者为 void。对 T 有附加要求的成员函数 (constructors, reset) 都明确地记录在下面。只要 T* 能被隐式地转换到 U*,则 shared_ptr 就能被隐式地转换到 shared_ptr。特别是,shared_ptr 隐式转换到 shared_ptr,当
6、 U 是 T 的一个可访问基类的时候,还能转换到 shared_ptr,以及转换到 shared_ptr。惯用手法由于智能指针是线程安全的,建议在代码里只为一个对象生成单一的shared_ptr,在多线程环境中传递参数时使用 weak_ptr,这样当 shared_ptr 被析构,weak_ptr 也会得到通知,变为无效,这样可以有效地防止野指针,同时也防止了潜在的循环引用,无法释放对象的问题。shared_ptr spA = shared_ptr(new A);int func(weak_ptr wpA)If(!wpA.expired())/正常处理逻辑shared_ptr spA = wp
7、A.lock();/Use spAelse/智能指针已被其他线程释放func(spA);2.2.1.3 智能指针 weak_ptr简介weak_ptr 是 Boost 智能指针 shared_ptr 的一个重要伙伴。它允许打破循环依赖。另外,它还可以处理一个非常常见的问题悬空指针。当销毁最后一个智能指针 shared_ptr,它会释放掉共享的资源。通过使用智能指针 weak_ptr,这方面的信息会传播给所有观察该共享资源的智能指针 weak_ptr,通过 weak_ptr可以知道该共享资源已经释放,这意味着不会发生无意间访问无效指针的情形。这就是观察者模式的一个特例,也就是说,当销毁资源的时候
8、,就会通知所有对资源感兴趣的 weak_ptr,通过调用 weak_ptr.expired(),当返回值为 true 时,就表示该共享资源已经释放了。weak_ptr 类模板存储一个引向已被 shared_ptr 管理的对象的 “weak reference“(弱引用)。为了访问这个对象,一个 weak_ptr 可以利用 shared_ptr 的构造函数或成员函数 lock 转换为 shared_ptr。当最后一个指向对象的 shared_ptr 消失,而对象也被删除后,从一个引向已被删除对象的 weak_ptr 实例获取 shared_ptr 的企图就会失败:构造函数会抛出一个 boost:
9、bad_weak_ptr 类型的异常,而 weak_ptr:lock 会返回一个 empty shared_ptr。每一个 weak_ptr 都符合 C+ 标准库的 CopyConstructible 和 Assignable 的必要条件,并因此能够用于标准库容器。因为提供了比较操作,因此 weak_ptr 可以和标准库中的关联式容器一起工作。weak_ptr 的操作绝不会抛出异常。这个类模板被 T 参数化,T 是被指向的对象的类型。相对于 shared_ptr,weak_ptr 提供了一个非常有限的操作子集,因为在多线程程序中访问它所存储的指针是非常危险的,甚至有时在一个单线程程序中也是不安
10、全的(也就是说,它可能引起未定义行为)。姑且假设 weak_ptr 有一个返回 raw pointer(裸指针)的 get 成员函数,考虑下面这个无辜的代码片段:shared_ptr p(new int(5);weak_ptr q(p);/ some time laterif(int * r = q.get()/ use *r设想在 if 之后,但是又恰恰在 r 被使用之前,另一个线程执行了语句 p.reset()。这样 r 就成了一个 dangling pointer(悬挂指针)。解决这个问题的方案是从 q 创建一个临时的 shared_ptr:shared_ptr p(new int(5)
11、;weak_ptr q(p);/ some time laterif(shared_ptr r = q.lock()/ use *r这样,r 就持有一个引向 q 所指向的对象的引用。即使在其它线程中执行了 p.reset(),那个对象也会继续活着,直到 r 离开作用域或者被 reset。通过获得一个指向这个对象的 shared_ptr,我们可以有效地保住它不被析构。2.2.2 正则表达式xpressive 是一个先进的、面向对象的、用于 C+的正则表达式模板库。正则表达式可以以字符串方式编写并在运行期分析,也可以以表达式模板方式编写并在编译期分析。正则表达式可以相互引用,或者递归引用其本身,你
12、就可以用它们来构建任意复杂的语法。它与 Boost.Regex 库的区别就是无需编译,全模板库。如果你要在 C+中处理文本,通常你有两个不相交的选项:正则表达式引擎或语法分析生成器。正则表达式引擎(如 Boost.Regex)更为强大和灵活;文本的模式以字符串方式表示,可以在运行期指定。但是,这意味着语法错误同样要到运行期才能被检测。另外,正则表达式不适合于高级的文本处理任务,如匹配平衡的、嵌套的标签。那些任务传统上都是由语法分析生成器(如 Spirit 语法分析器框架)来处理的。这些工具更为强大,但不够灵活。它们通常不允许你随时随意地修改你的语法规则。此外,它们不具备正则表达式的完全回溯语义
13、,对于某些类型的模式来说,这样对作者更具有挑战性。将这两种方法无缝地集合到一起,在 C+的文本处理世界中占据了独特的优势。通过 xpressive,你可以选择更象使用 Boost.Regex 那样去使用它,将正则表达式表示为字符串。或者你也可以象使用 Spirit 那样使用它,将你的 regexes 写为 C+表达式,获得一种专用于文本处理的嵌入式语言所带来的所有好处。更重要的是,你可以混用这两种方式,从而获得两者的好处,对于那些要静态绑定的正则表达式编写正则表达式 语法 - 由编译器进行硬编码和语法检查 - 其它的则动态绑定并在运行期指定。这些正则表达式可以相互递归地引用,在以前的正则表达式
14、不能进行模式匹配的字符串中进行模式匹配。Xpressive 是一个只含头文件的模板库,这意味着你不需要修改你的构建脚本或链接任何独立的 lib 文件就可以使用它。你所要做的就是 #include 。如果你只使用静态 regexes,你可以只包含 xpressive_static.hpp 以提高编译速度。同样,如果你计划只使用动态regexes,则可以只包含 xpressive_dynamic.hpp。表 28.1. xpressive 的工具箱Tool 工具 Description 说明 basic_regex 是 xpressive 之中最重要的类型。你用 xpressive 做的任何事情都
15、要从创建一个类型为 basic_regex, sub_match 含有 regex_match() 或 regex_search() 操作的结果。它就象一个存有 sub_match 对象含有一个已标记的子表达式(在 Perl 中又称为后向引用)。基本上,它只是一个迭代器对,代表了已标记子表达式的开始和结束。 regex_match() 检查一个字符串是否匹配一个regex。 regex_match() 要成功,必须是 整个字符串 从头到尾匹配 regex。如果你给了 regex_match() 一个 match_results,那么它会将所有找到的带标记子表达式写入其中。 regex_repl
16、ace() 给定一个输入字符串,一个 regex 和一个替代字符串,regex_replace() 通过将输入字符串中与 regex 相匹配的部分替换为替代字符串来构建一个新的字符串。替代字符串可以含有对带标记子表达式的引用。 regex_iterator 会返Tool 工具 Description 说明 回一个 match_results 可以找出下一个匹配。 regex_token_iterator,除了一点,解引用一个 regex_token_iterator 一个用于 basic_regex,因为 basic_regex。不过,如果你需要做一些花哨的东西,如创建一个带有不同 std:l
17、ocale 的 basic_regex 了。 现在你应该对 xpressive 所提供的工具有了一些了解,你可以通过回答以下两个问题来找到合适的工具: 1. 你要用哪种类型的迭代器来遍历你的数据? 2. 你要对你的数据做什么操作? 弄明白你的迭代器类型 在 xpressive 中的多数类都是根据迭代器类型参数化的模板类。xpressive 定义了一些常用的 typedefs 来让你可以更容易地选择合适的类型。你可以用下表基于你的迭代器类型来找到正确的类型。 下表 为 xpressive Typedefs 与迭代器类型std:string:const_iterator char const *
18、std:wstring:const_iterator wchar_t const * basic_rege sregex cregex wsregex wcregex std:string:const_iterator char const * std:wstring:const_iterator wchar_t const * x smatch cmatch wsmatch wcmatch regex_compiler sregex_iterator cregex_iterator wsregex_iterator wcregex_iterator regex_token_iterator
19、(delim= (set= /,-) / 后跟一个分隔符. (day= repeat(_d) delim / 和一个日期再跟一个分隔符. (year= repeat(_d _d); /和年份。cmatch what;if( regex_search( str, what, date ) )std:cout what0 n; / whole match 全匹配std:cout whatday n; / the day 日期std:cout whatmonth n; / the month 月份std:cout whatyear n; / the year 年份std:cout whatdelim n; / the delimiter 分隔符