Boost程序库完全开发指南——深入C++“准”标准库(第3版)(txt+pdf+epub+mobi电子书下载)


发布时间:2020-07-12 19:14:57

点击下载

作者:罗剑锋

出版社:电子工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Boost程序库完全开发指南——深入C++“准”标准库(第3版)

Boost程序库完全开发指南——深入C++“准”标准库(第3版)试读:

推荐序

最近一年我电话面试了数十位C++应聘者,惯用的暖场问题是“工作中使用过STL的哪些组件?用过Boost的哪些组件?”得到的答案大多集中在vector、map和shared_ptr。如果对方是在校学生,我一般会问问vector或map的内部实现、各种操作的复杂度,以及迭代器失效的可能场景。如果是有经验的程序员,我还会追问shared_ptr的线程安全性、循环引用的后果及如何避免、weak_ptr的作用等。如果这些都回答得不错,进一步还可以问问如何实现线程安全的引用计数,如何定制删除动作等。这些问题让我能迅速地判别对方的C++水平。

我之所以在面试时问到Boost,是因为其中的许多组件确实可以用于编写可维护的产品代码。Boost包含近百个程序库,其中不乏具有工程实用价值的佳品。每个人口味与技术背景不一样,对Boost的取舍也不一样。就我的个人经验而言,首先可以使用绝对无害的库,例如noncopyable、scoped_ptr、static_assert等,这些库的学习和使用都比较简单,容易入手。其次,有些功能自己实现起来并不困难,正好Boost里提供了现成的代码,那就不妨一用,比如date_time和circular_buffer等。再次,在新项目中,对于消息传递和资源管理可以考虑采用更加现代的方式,例如用function/bind在某些情况下代替虚函数作为库的回调接口、借助shared_ptr实现线程安全的对象回调等。这二者会影响整个程序的设计思路与风格,需要通盘考虑,如果正确使用智能指针,在现代C++程序里一般不需要出现delete语句。最后,对某些性能不佳的库保持警惕,比如lexical_cast。总之,在项目组成员人人都能理解并运用的基础上,适当引入现成的Boost组件,以减少重复劳动,提高生产力。

Boost是一个宝库,其中既有可以直接拿来用的代码,也有值得借鉴的设计思路。试举一例:正则表达式库regex对线程安全的处理。

早期的RegEx类不是线程安全的,它把“正则表达式”和“匹配动作”放到了一个类里边。由于有可变数据,RegEx的对象不能跨线程使用。如今的RegEx明确地区分了不可变(immutable)与可变(mutable)的数据,前者可以安全地跨线程共享,后者则不行。比如正则表达式本身(basic_regex)与一次匹配的结果(match_results)是不可变的;而匹配动作本身(match_regex)涉及状态更新,是可变的,于是用可重入的函数将其封装起来,不让这些数据泄露给别的线程。正是由于做了这样合理的区分,RegEx在正常使用时就不必加锁。

Donald Knuth 在“Coders at Work”一书里表达了这样一个观点:如果程序员的工作就是摆弄参数去调用现成的库,而不知道这些库是如何实现的,那么这份职业就没啥乐趣可言。换句话说,固然我们强调工作中不要重新发明轮子,但是作为一个合格的程序员,应该具备自制轮子的能力。非不能也,是不为也。

C/C++语言的一大特点是其标准库可以用语言自身实现。C标准库的strlen、strcpy、strcmp系列函数是教学与练习的好题材,C++标准库的complex、string、vector则是类、资源管理、模板编程的绝佳示范。在深入了解STL的实现之后,运用STL自然手到擒来,并能自动避免一些错误和低效的用法。

对于Boost也是如此,为了消除使用时的疑虑,为了用得更顺手,有时我们需要适当了解其内部实现,甚至编写简化版用作对比验证。但是由于Boost代码用到了日常应用程序开发中不常见的高级语法和技巧,并且为了跨多个平台和编译器而大量使用了预处理宏,所以阅读Boost源码并不轻松惬意,需要下一番功夫。另外,如果沉迷于这些有趣的底层细节而忘了原本要解决什么问题,恐怕就舍本逐末了。

Boost中的很多库是按泛型编程的范式来设计的,对于熟悉面向对象编程的人而言,或许面临一个思路的转变。比如,你得熟悉泛型编程的那套术语,如concept、model、refinement,才容易读懂Boost.Threads文档中关于各种锁的描述。我想,对于熟悉STL设计理念的人而言,这不是什么大问题。

在某些领域,Boost不是唯一的选择,也不一定是最好的选择。比如,要生成公式化的源代码,我会首选用脚本语言写一小段代码生成程序,而不用Boost.Preprocessor;要在C++程序中嵌入领域特定语言,我会首选用Lua或其他语言解释器,而不用Boost.Proto;要用C++程序解析上下文无关文法,我会首选用ANTLR来定义词法与语法规则并生成解析器(parser),而不用Boost.Spirit。总之,使用Boost时心态要平和,别较劲去改造C++语言。把它有助于提高生产力的那部分功能充分发挥出来,让项目从中受益才是关键。

要学习Boost,除阅读其官方网站的文档、示例与源码之外,最好能有一本比较全面的中文书在手边随时翻阅。对于不谙英文的开发者,这更是可幸之至。您手上这本《Boost 程序库完全开发指南》是很好的使用指南与参考手册。作者由浅入深地介绍了Boost的大部分常用内容,能让读者迅速了解Boost,并从中找到自己需要的部分。拿到这本书稿之后,我有粗有细地阅读了一遍。总体来看,作者水平很高,也相当务实,对C++和Boost的理解与运用很到位,我从这本书学到了不少新知识。为此,我乐于向希望学习Boost程序库的开发者推荐这本靠谱的书。

须知“功不唐捐”,作为一名现代C++程序员,在Boost上投入的精力定能获得回报。陈硕《代码大全》译者之一中国·香港

第3版前言

随感

弹指一挥间又过去了两年,所谓“一鼓作气,再而衰,三而竭”,本书能够出到第3版,实在是有些诚惶诚恐。

回想当初撰写本书的动机,完全是凭着一腔热血、一股子冲劲,只是想整理出自己的Boost学习经验,没有太多名利的考虑。让我没有想到的是,这四年来这本书确确实实地影响了我的人生——因为它我结识了很多朋友,也见到了很多令人敬仰的大师,从他们身上我学到了许多东西,受益匪浅。

如今的第3版则是我的再一次出发,心态上已不复当时的冲动,更多的是一种责任,一种把C++和Boost的最新进展如实记录并奉献出来的责任。

对第2版的改动

时至今日C++11标准发布已三年有余,C++14也已经滚烫出炉,C++17则蓄势待发,标准委员会的努力工作让近不惑之年的C++语言不仅没有显出老态,反而愈加充满活力,继续在计算机的各个领域大展拳脚。

本次修订的目标之一是向C++11/14标准靠拢,使用现代的C++编程范式,所以较前两版更多地使用了新标准里的新特性,读者在阅读时需要留意。

第2版使用了四个开发环境,处理不同系统之间的兼容问题令人头疼,特别是要“照顾”不支持C++11标准的编译器而不能使用新特性,编写代码有种“捆住了手脚”的感觉。故本版只使用Linux操作系统,编译器也使用最新的GCC4.8,简化了开发环境的配置,也可以让本书不至于在今后的几年里落后于时代。

本书第3版的其他重大变化列举如下:

■ 第0章:重新组织了结构,增加GitHub源码资源;

■ 第1章:新增构建工具b2的介绍,删除了STLPort和Windows相关内容;

■ 第3章:新增make_unique()/intrusive_ptr等很多新内容,删除了auto_ptr相关的阐述;

■ 第4章:新增ignore_unused、explicit_operator_bool等内容,更新optional和assign库,删除了typeof;

■ 第5章:新增string_ref库,更新了lexical_cast;

■ 第6章:新增lightweight test,调整了assert;

■ 第8章:新增clamp、hex等算法;

■ 第9章:新增math.constants和ratio库,调整了rational;

■ 第10章:新增chrono库,删除了io_state_savers;

■ 第11章:增加对C++11里lambda表达式的论述,删除了result_of;

■ 第12章:新增atomic库,完全重写了thread和asio,增加大量新内容,如UNIX信号、协程等;

■ 第13章:更新至Boost1.57版,删除了原13章boost.python;

■ 第14章:新增了一个对象池模式;

■ 附录:完善对 C++11/14/17 标准的介绍,删除了 STL 简介。

致谢

前两版的致谢已经够多了,但我还是要感谢可敬可爱的读者,希望本书依然是您工作中不可或缺的伙伴。罗剑锋2015年1月15日于北京

第2版前言

本书第1版面世至今忽忽然不觉已经两年有余,其间多蒙读者厚爱,褒奖有加,不胜感激,在此聊写些文字,以志心性。

闲谈碎语C++

这两年里对于C++社区来说最重大的事件莫过于是C++11标准的发布了:历经十余年的磨砺,崭新的C++终于扬刀出鞘,诸多新语言特性和库的加入令C++旧貌换新颜,从此程序员手里的这把宝刀更是增添了无穷的威力,上天入地屠龙伏虎,不在话下。

最近开源界的一桩新闻也不得不提一下:著名的C/C++编译器GCC于2012年8月完成了从C实现到C++实现的转换。虽然这件事比不上C++11发布,但足可以从一个侧面证明C++的实力已经得到了开源界的高度认可,今后没有什么是C++做不出来的了。

作为C++标准的后备,随着C++11的正式发布,Boost程序库现在进入了一个新的历史时期:一方面依据新标准不断完善自身,另一方面则秉承传统继续开拓C++11未涉及的领域。这两方面可以从Boost历次巨细靡遗的更新记录中看出来——不断修正既有库中不符合标准的地方,同时再谨慎地引入新的组件,“小步快跑”地奔向“康庄大道”。

由于Boost程序库正逐渐向C++11标准靠拢,曾经的“准标准库”美誉已经不太合适了,它更像是一个比C++标准库更加“兼容”、更加“标准”的“超级标准库”——使用Boost可以完全消除C++11和C++98之间的差异,稍微有点夸张地说:学习Boost就相当于学习现代C++,使用现代C++必然避不开Boost。

国内外的许多公司都已经把Boost作为自己源码资产的一部分,在这些高质量的软件组件之上开发产品,更激进的如Facebook则是走在了潮流的前面,同时使用最新的C++11和Boost来开发软件(如开源的folly)。面对新技术,我们应该克服心中的怯懦和懒惰,积极学习,力争“站在C++98的最高峰沐浴C++11的阳光”。

对第1版的改动

本书第1版撰写于2010年年初,彼时C++新标准尚未确定,国内了解、使用Boost的人也不多,故书的内容偏重于入门和介绍。而现在的情况则已经大有不同:C++11标准相当于是一个全新的语言,获得了诸多编译器生产商的积极响应,移动互联网的兴起也使得国内的程序员群体开始越来越多地关心起C++和Boost,整体C++水平有所提升。

鉴于这些变化,第2版在保持原书风格的同时做了适当调整,删去了一些浅显的部分,并依据最新的C++11和Boost程序库全面更新,较第1版略增加深度,但仍然是以入门为主,不过多介绍库的实现细节。由于C++11标准有很多新的语言特性和库,包含了部分Boost库原有的功能,故作者弱化了一些与C++11语言特性重复的库,对于库组件则着重讲解符合C++11标准的功能。[1]

本书第2版几乎每页都较前一版有修改,各章的重大变化列举如下。

■ 第1章:重新组织了结构,分别介绍UNIX和Windows开发环境;

■ 第3章:增加对unique_ptr的介绍,补充完善对weak_ptr的论述;

■ 第4章:typeof库推荐改用C++11的auto,删除4.11.3节;

■ 第6章:增加对C++11 static_assert关键字的介绍;

■ 第7章:array、unordered、tuple库更新为C++11标准;

■ 第8章:foreach推荐改用C++11的for,minmax库更新为C++11标准;

■ 第9章:random库更新为C++11标准;

■ 第10章:新增cpu_timer库,system更新为C++11标准,filesystem更新为V3;

■ 第11章:result_of、ref、bind、function等库更新为C++11标准;

■ 第12章:thread库更新为符合C++11标准的V3(变动非常大);

■ 第14章:更新至Boost1.51版;

■ 附录:补充了对 C++11 标准的简介,同时删除了用处不大的网络资源和ref_array源代码。

第1版的版式个人感觉比较满意,但代码行多的时候还是不容易阅读,所以本次对部分重要的代码片段加粗醒目,用于提醒读者注意要点。

作者最早的开发环境是Windows,近年来工作重心逐渐转移,Mac OS X和Linux成了主力工作环境(个人非常欣赏Apple公司),因此第2版中淡化了Windows操作系统的色彩,不再细述VC相关的配置,示范代码也基本改为UNIX风格,请读者明鉴。

致谢

同第1版一样,我首先要感谢Bjarne Stroustrup博士、C++标准委员会和C++/Boost社区,感谢他们长久以来的坚持和努力,为我们带来了C++11标准和越来越接近完美的Boost程序库。

然后是我的家人:感谢我的父母、弟弟和妻子,你们永远是我生命中最重要的人;感谢即将满4岁的女儿,这本书是和你一同成长起来的,希望你将来能够读懂它。

最后感谢读者选择本书,希望和您再次一起分享学习C++和Boost的快乐。罗剑锋2012年10月12日于北京WFC

[1]C++11标准虽然已经公布一年有余,但可能是因为内容太过庞杂,作者尚未见到有全面完整论述的专著,书中对C++11的理解难免有错漏不足,还请读者见谅。

第1版前言

屈指算来,接触C++语言至今已经有十余个年头了。回首往事,不禁感慨良多。

缘起

1996年我上大学最开始学的是Pascal,不得不说,Pascal严谨的程序风格确实很适合作为一门教学语言,然而用于实际开发就不那么合适了(直到出现Delphi)。由于当时学校并未开设C语言课程,因此在Pascal课程结束后我就买书自学C/C++语言,并在次年报名计算机软件专业技术资格和水平考试,靠着一点点编程和考试的“天分”获得了高级程序员资质(当年很热衷考级考证,后来就“淡定”多了)。虽然有了资格证,但我仍然算是个C++的初学者,对于C++的认识还处于C的面向过程和简单的基于对象层次上。

新千年伊始我考入了北京理工大学就读研究生,因为跟导师做项目开始接触STL与C++标准库,大概是2005年从1.33版结识了Boost,这才真正领略了C++的精髓。那段时期Java和C#正在国内大行其道,C++则势单力薄,有关STL和C++标准的技术书籍寥寥无几,而讲解Boost的书更是为零,故对Boost的学习基本只能靠自己的摸索与实践。好在Boost自带的文档相当丰富(尽管看全英文的资料十分辛苦),而且源码也写得比较清晰规范,在熟悉了STL的基础上学习Boost倒也并不算太难。

但Boost的一个最大的特点就是“庞大”,功能组件众多,要想把它全部装进脑子里融会贯通基本上是不可能的,使用时需要经常查阅英文文档,相当麻烦。因此,在学习的过程中,我逐渐产生了编写学习笔记的想法。一开始只是一个简单的纯文本文件,记录了一些使用经验的片断。随着积累不断增加,纯文本形式已经不能够满足知识整理的需求了,于是我又把这些文字迁移到了Word文档里,把使用经验分类编目,进行较系统的归纳梳理。慢慢地,这份学习笔记居然有了上万字的规模,成为了一份很好的Boost备忘参考,在日常的开发工作中给了我很大的帮助——就像《设计模式》一书中所说的那样,捕获了很多使用Boost解决问题的实践经验,避免了重复发现。不过,这份资料一直仅限于我个人使用,属于“自娱自乐”的作品,从未示人。

时间一晃到了2010年1月份的某天夜里,不知道是什么原因我忽然失眠了,躺在床上翻来覆去怎么也睡不着。突然,一个念头闯入了脑海:把Boost开发经验整理出版吧,让更多人能够分享这些知识,正所谓“独乐乐,与众乐乐,孰乐?”这个大胆的想法的出现让我那天的失眠又延长了几个小时——关于书的各种构想在头脑中“肆虐横行”。

随后的几天里我就把这个想法付诸行动了,虽然以前也写过并发表很多文章,也在网上印刷了几本个人文集,但出版正式的书还是第一次。在把学习笔记进一步整理完善,编写出较完整的结构和一个样章后,我就开始联系出版社了。当初并没有多大的信心,毕竟我这个作者名不见经传,也没有什么资历、背景和名气(而且还是个“网盲”,从未跟随潮流开个人博客)。很幸运,发出的第一个E-mail就是电子工业出版社,而且编辑也在第一时间回复了我,这才给了我持续写作完成全书的动力。

写作过程中我也进一步加深了对Boost的认识,澄清了许多原来未曾注意到的细节。原本只打算写20万字左右、300多页,但写到中途才发现Boost库的博大精深远非当初的理解,也意识到了自己当初学习的肤浅。历经了近半年近乎不眠不休的努力,最终呈现给读者的是这本厚达500多页的图书,文字量是最初学习笔记的数十倍,内容也翔实丰满了很多——达成这个结果,我个人可以说是问心无愧了。

C++与Boost

C++较Java和C#等语言的一个最大不同在于它并非是由某个公司或个人把持的,它的真正发展动力来自于广大程序员。Boost就是这样的一个典范,它成功地填补了从C++98到C++0X这“失落的十年”间的空白,在竞争对手Java和C#不断更新版本新增特性的时候以库的形式极大地增强了C++的能力,使C++不至于因为标准规范的滞后而落后于时代;而且Boost还深层次地挖掘了C++的潜力,开创了泛型编程、模板元编程、函数式编程等崭新的境界。

就个人来说,我比较喜欢的Boost版本有两个,分别是1.35和1.39。1.35版增加了asio、bimap、circular_buffer等许多重要组件,而1.39版则增加了signals2库,这两个版本都在我的工作用机上停留了相当长的时间。落笔之时,Boost已经更新到了1.43版,成长为一个相当完善、全面、强大的C++程序库。可以毫不夸张地说,现在的C++程序员,如果不熟悉Boost,那么至少丧失了一半使用C++的好处,同时会多耗费数倍的开发精力和时间。

随着C++0X标准的即将来临,Boost程序库的发展也出现了加速的趋势,由原来间隔数月不定期更新版本,改为定期(每3个月左右)发布新版本,而且每个新版本都会包含大量极有价值的更新内容。因此,希望读者在阅读本书时及时访问Boost的官网(http://www.boost.org),以便获取最新的版本。

感谢读者选择本书,再说一句真心的“套话”(笑):限于作者水平有限,书中错漏在所难免,敬请读者原谅、指正。

致谢

首先我要感谢整个C++群体,特别是:C++语言的发明者Bjarne Stroustrup博士——他给我们带来了美妙的C++;然后是Alexander Stepanov和C++标准委员会——他们把STL引入了C++,开创了C++的现代编程风格;以及Beman G.Dawes、Boost程序库的所有作者和Boost社区——他们为我们奉献了如此高水准的程序库。

其次我要感谢电子工业出版社博文视点公司,他们给了我这个把自己的开发经验出版成书的机会,在把潦草的个人学习笔记变成正式图书的过程中他们付出了艰辛的努力。还要感谢陈硕先生,他审阅了本书的部分手稿,提出了很多有价值的参考意见,并慨然为本书撰写序言。

接下来我要感谢我的家人:感谢我的父母和弟弟,他们永远是我生命中最重要的人;感谢我的妻子,她自始至终都支持我的写作,并担负了大部分照顾孩子的家务(虽然偶有怨言);还要对已满一岁半的女儿说声抱歉,为了写作本书,我已经牺牲了很多陪她玩耍的时间。

我还要感谢黄美华、冯薇、戚天龙、罗玉震、颜静、陈刚、张秋香、缪泽波等同事,长期的共事令我们建立了深厚的友谊。对后两位同事致以特别的感谢,他们对完成本书提供了大力的支持和帮助。

最后,感谢多年以来的好友岳大海、时吉斌、王峰,感谢我的中学老师邓英、杜爱芹、练鑫云、陈静,感谢我的研究生导师贾云得,以及所有在我成长过程中曾经给予我关心和帮助的朋友们!罗剑锋2010年6月7日于北京王府井第0章导读0.1关于本书

C++是一种伟大的编程语言,某种程度上它甚至超越了编程语言的境界而升华为一种哲学。

C++又是一种多范式、可扩展的编程语言,支持多种编程风格——基于过程、基于对象、面向对象、函数式、泛型、模板元、自动机,非常自由灵活,易学难精,在不同编程风格间切换时必须小心谨慎以避免失误。

C++标准中出现的STL(标准模板库)极大地改变了C++程序员的编程思维,使“泛型”成为了21世纪以来程序开发界最流行的词汇之一。而C++标准委员会成员所设立的Boost社区和开发的Boost程序库,更将“泛型”等现代C++编程方法发挥到了极致。

Boost程序库代表了目前C++语言最新最前沿的技术,内容博大精深,丝毫不逊于经典的STL,但同时也令很多人难以摸清门路,不得登堂入室而一窥究竟。有鉴于此,作者根据多年在实际开发中使用Boost库的经验并结合最新的C++11/14标准编写了本书,想为广大C++程序员了解C++的最新技术进展尽一份自己的力量。

本书的定位是“指南”(Guide、Introduction),而不是技术手册(Reference)或者使用说明(Manual),能够解答90%但不是所有Boost库相关的问题。但作者尽量做到让本书接近一本参考手册,使读者在阅读时能够脱离计算机,不至于频繁使用鼠标和键盘查询自带在线帮助或者源代码。0.2读者对象

本书适合有一定C++基础的读者。

阅读本书需要有一定的C++知识,基本要求是了解面向对象编程技术,了解封装、继承等面向对象程序语言的特性,此外还要理解名字空间、异常、模板[1]、泛型编程等C++高级特性(但不必非常深入地了解),最好还能够了解设计模式和C++标准库提供的容器、算法等各种组件。

如果读者是C++初学者或还不具备以上所列的知识,建议先阅读附录A的推荐书目所列的技术书籍,然后再学习本书。如果手头刚好有推荐书目中的一本或两本,则可以一边翻阅附录A的推荐书目中的书籍,一边学习本书。

总之,无论读者是C++哪一层次的用户,现在或将来本书都会给您带来帮助。0.3术语与风格

本节列出了本书中经常用到的专业术语和编程风格,以期与读者获得阅读的共识。

Boost库并不是一个单一、平面化的程序库,而是有着复杂的内部结构,每个“库”可能是由其他许多更小的“库”组成的。因此,本书中把程序库中所有有机组成部分统称为“组件”,“库”(Library)与“组件”(Component)这两个术语有时会通用。

namespace这个C++术语有译称作“命名空间”、“名称空间”、“名字空间”,本书依据推荐书目[4][5]称作“名字空间”。这只是作者个人习惯而已,如果在阅读过程中给读者造成了小小的困扰,还请谅解。

在使用template定义模板类或者模板函数时,本书统一使用typename而不是常见的class,因为typename能够更清楚地向代码阅读者表明这是一个类型参数,而不一定是一个类(class)。但例外的是书中列出Boost源代码,会尽量保持其原始形式。

在命名函数或者类时,本书遵循C++标准库和Boost的惯例,均采用小写形式,单词间以下划线分隔,如demo_class、rand_bytes(),但并不要求读者也必须遵循这种命名方式,通常自己编写的类使用大写字母开头的单词命名会更好。

在for循环递增变量、指针或者迭代器对象的时候,本书统一使用++操作符的前置用法(++i),而不是后置用法(i++),因为前者不需要返回一个临时对象,执行效率更高。“未定义行为”一词经常用来指代某些操作可能导致的不正确结果,如使用已失效的迭代器、错误地使用指针等等。对于“未定义行为”的一个较好(但不太精确)的定义是:程序在开发人员面前运行正常,在测试人员面前运行正常,但在老板或者最终用户面前运行时崩溃了。读者应当小心并尽量避免“未定义行为”,它是代码中的“定时炸弹”,如果它在调试的过程中爆炸了,那通常是最好的结果,因为它明确地告诉了你代码存在问题。0.4C++标准

现在C++有三个国际标准:C++98、C++11和C++14[2],本书以不含数字标识的“C++标准”一词泛指这三个标准中的任意一个,但在涉及C++某些语言特性时可能会明确指出具体的版本。

本书主要使用C++11标准,有时会以“C++11.x.y.z”的形式标明所引用标准文档(ISOIEC 14882 C++11)的章节号。书中使用较多的C++11新语言特性有四个:

■ nullptr:强类型的空指针;

■ auto/decltype:自动推导表达式类型;

■ for:基于范围的新式循环,形式更加优雅;

■ lambda表达式:又称闭包(closure),能够非常便捷地定义函数对象,通常的形式是[](...){...},其中[]是捕获列表,()是函数参数,{}是函数体。

C++标准中定义的函数库本书称为“C++标准库”或者“STL”,但严格意义上STL与标准库并不等价,STL只是标准库中的一个(很大的)子集,这么称呼有时候只是为了行文上的方便。

一般情况下使用C++标准库都必须包含相应的头文件并且加上using namespace std语句,但标准库已经成为了C++软件开发的基础设施,应用得非常频繁,因此书中的代码示例片断一般会将之略去。但有的情况下为了特别强调,会加上std名字空间前缀,如std::vector。读者可以认为书中所有代码都默认包含了如下的头文件:

为使读者对C++标准能够有更多的了解,作者编写了一份C++标准的简要介绍,作为本书附录供参考。0.5本书的结构

Boost库组件繁多,相互关联也较多,如何排列其顺序是作者面临的一个颇为棘手的问题。

Boost官方提供了两种基本方式:一种是按照组件的字母顺序,另一种是按照功能用途分类顺序,但这两种方式都不是组织本书结构的最佳手法。经反复斟酌,作者决定以难易度和实用程度对Boost库组件分类排序,采用由浅入深循序渐进的方式,先介绍较简单易用且实用程度高的库,然后逐步深入,介绍用法复杂的库,以期帮助读者尽快掌握Boost的使用方法。

对于每个Boost组件,本书通常首先简要介绍其功能,然后说明其头文件和编译方法(如果需要编译的话),列出类的声明概要,再使用例子讲解详细用法和注意事项,涉及其他Boost库组件时则以交叉引用的方式指明参考章节,最后是对该库的总结。

本书共分16章,每章的内容简介如下:

■ 第0章:导读

也就是读者正在阅读的这一章,介绍本书的基本内容和一些注意事项。

■ 第1章:Boost程序库总论

本章简要介绍Boost的历史、特点和获取方式,以及本书的开发环境和如何编译安装Boost。

■ 第2章至第13章

第2章至第13章分门别类、由浅入深地介绍Boost库的各个组件,占据了本书的大部分篇幅,也是读者需要仔细阅读的内容。其中既包括如timer、swap等简单的小工具,也包括test、thread、asio等用法复杂且功能强大的组件,Boost 1.57版全部129个库在本书中都可以找到阐述。

■ 第14章:Boost与设计模式

本章结合之前介绍的所有Boost库组件简要论述了推荐书目[1]中的23个设计模式和4个其他常用模式,以及Boost库组件使用这些设计模式的方法,从设计模式的抽象层次来加深理解Boost库。

■ 第15章:结束语

本章简单展望了Boost今后的发展,介绍了其他可与Boost互为补充的开源C++库,并对如何做一个好的程序员提出了一些自己的见解。

■ 附录

书末的附录也很有价值,列出了作者认为值得阅读的C++经典书籍——它们也是作者编写本书时的案头必备参考资料,还有就是C++标准简述和编程经验谈。

这些内容被放在附录部分并不是因为它们不重要,而仅仅是因为它们与全书的主题关联不大,但很值得阅读,读者会从中发现一些很有用的东西。0.6如何阅读本书

本书是一本介绍程序库的书籍,其中的很多组件是彼此独立的,所以在阅读完第0章(即本章)和第1章后,可以随意愿自由阅读其他章节。

读者既可以按照书的物理顺序循序渐进地逐页阅读,也可以查阅目录,然后直接跳到感兴趣的章节。不过对于大多数读者来说,作者还是推荐第一种方式,因为这可能会是学习效率更高、学习曲线更平滑的方式。

涉及编程语言,尤其是C++,书中不可避免地会出现大量的代码片断,有的还可能很长,希望读者阅读时能有足够的耐心。这些代码都是精心编制的范例,将会指导读者如何使用Boost编写正确、高效的代码。

阅读时还需要注意一点:本书并不包含Boost库的所有接口说明,如果在阅读过程中对某些地方有疑虑,请参考Boost说明文档或者直接阅读Boost实现代码。0.7本书的源码

为了更好地方便读者利用本书学习研究Boost程序库,作者在GitHub网站上发布了本书内所有示例程序的源代码,读者可以根据需要自取。[3]

本书源码的GitHub项目地址是:

https://github.com/chronolaw/boost_guide.git

最后,欢迎来到Boost的世界!!!

[1]模板类和模板函数一般形式是class_or_func,例如vector。<>在编程语言中通常被用于大小比较,因此其形式对于初学者很难接受,但这种形式具有良好的可读性,<>可以被读作英文of,比如vector可以读作vector of int,清晰地表明了类/函数和它的模板类型的关系,这样可以减少一部分对尖括号的不适应感,作者经常这么做,读者也可以试一下,很有效。

[2]实际上还应该有一个C++03标准,但因为它与C++98的差异很小,故本书忽略之。

[3]有了GitHub,再也不需要“附赠光盘”了,笑。第1章Boost程序库总论

Boost是一个功能强大、构造精巧、跨平台、开源并且完全免费的C++程序库。

本章将带领读者快速浏览Boost程序库,了解它的历史、组成和基本使用方式,并在Linux上搭建自己的开发环境,为之后的学习做准备。1.1关于Boost

1998年,Beman G.Dawes(C++标准委员会成员之一)发起倡议并建立了Boost社区,目的是向C++程序员提供免费的、同行审查的、可移植的高质量C++源程序库。Boost强调程序库要与C++标准库很好地共同工作,建立在“既有的实践”之上和提供参考实现,使得Boost库可以适合最后的标准化。自创立以来,Boost社区的工作已经取得了卓越的成果,C++11标准库中有三分之二来自Boost库,而且将来还会有更多的库进入新标准。

C++三十余年的发展历史中产生了数不清的程序库,有影响力的也不计其数,然而没有一个能够与Boost相提并论,Boost有着与其他程序库无法比拟的优点:

■ 首先,许多Boost库的作者本身就是C++标准委员会成员,因此Boost天然成为了标准库的后备,负责向新标准输送组件,也使得Boost获得了“准”标准库的美誉[1]。

■ 其次,Boost独特的同行审查制度保证了每一个Boost库组件都经过了严格的审查和验证,使库具有很高的工业强度,甚至超过大多数商业产品的实现。Boost 库采用了类似STL的编程范式,但却并没有STL那样晦涩难懂,代码格式优美清晰、易于阅读,而且附带丰富的说明文档——它既是一个程序库,同时也是一个很有价值的学习现代C++编程的范本。

■ 最后,Boost 的发布采用 Boost Software License,这是一个不同于 GPL、Apache 的非常宽松的许可证,允许库用户将 Boost 用于任何用途,既鼓励商业用途,也鼓励非商业用途。用户无须支付任何费用,不受任何限制,即可轻松享有Boost的全部功能。

本书内容基于Boost官方于2014年11月发布的Boost1.57版,共包含129个库/组件,分为25大类,涵盖字符串与文本处理、容器、迭代器、算法、图像处理、模板元编程、并发编程等许多领域——使用Boost,将大大增强C++的功能和表现力。

虽然本书主要讨论Boost1.57版,但由于Boost库中的许多组件已经相当稳定,故书中的论述对1.57版之前和之后的版本也基本适用。1.1.1 获取方式

Boost提供源码形式的安装包,可以从Boost官方网站(http://www.boost.org)下载最新版本——本书使用的是boost_1_57_0.tar.gz,把该文件解压缩到磁盘任意位置即可,例如:

tar xvfz boost_1_57_0.tar.gz#解压缩到当前目录1.1.2 目录结构

Boost压缩包解开后有近5万个文件,占据约600M的磁盘空间,但目录结构却很简洁清晰:

大多数情况下我们只需要关心boost子目录,这里以头文件的形式分门别类存放了我们要使用的库代码:1.1.3 使用方式

Boost库大多数组件不需要编译链接,我们在自己的源码里直接包含头文件即可。例如,如果要使用boost::tribool,只需要在C++源文件中添加如下include语句(当然,接下来的代码可能还需要using namespace boost;):

#include //使用tribool库

细心的读者会发现,Boost库的头文件与我们平常所用的头文件(*.h)或C++标准库头文件(没有后缀名)不同,这正是Boost的独特之处。它把C++类的声明和实现都放在了一个文件中,而不是分成两个文件,也就是“.h+.cpp”,故文件的后缀是.hpp。[2]

剩下的少量库(如chrono、date_time、regex、program_options、test、thread、python等)必须编译成静态库或者动态库,并在构建时指定链接选项才能使用。不过有个好消息,其中有的库不需要编译也可以使用部分或全部功能,而更好的消息是有的库已经有了不需要编译的替代品。1.2开发环境

阅读本书和使用Boost,读者需要一个能够较好地支持C++标准(至少是C++98标准)的操作系统和编译器,之后才能编译和安装Boost程序库。1.2.1 操作系统和编译器

C++是一个大型语言,十分复杂。虽然C++98标准已经面世十余年,C++11标准也已经出台三年多,但仍然没有一个编译器敢宣称自己能够100%支持C++的全部特性。

由于Boost大量使用了C++高级特性(如模板偏特化、ADL),因此不是所有的编译器都能够很好地支持Boost,并且每个组件对编译器的支持都不尽相同。虽然Boost已经针对平台和编译器的兼容性做了大量的工作,但仍有可能出现意外情况。随着Boost版本的增长,某些过“老”的编译器(例如VC6&7、GCC3)和CPU已经不再被支持。

本书作者使用的操作系统是Ubuntu 14.04(Linux 3.13.0),编译器使用自带的GCC 4.8.2(对C++11标准的支持较完善),所有代码均在这个开发环境中编译通过。[3]1.2.2 快捷安装Boost

Linux操作系统下安装Boost很容易,最简单省事的方法是在Boost解压缩后的目录下直接执行命令:

第一条命令bootstrap.sh是编译前的配置工作,第二条命令b2开始真正的编译并安装Boost。[4]

如果像上面这样不指定额外选项,Boost将编译release版本的库文件,把头文件安装到/usr/local/include,库文件安装到/usr/local/lib。1.2.3 完全安装Boost

我们也可以完整编译Boost,使用buildtype选项指定编译类型(如不指定则默认使用release模式),在bootstrap.sh之后执行如下命令:

这样将开始对Boost的完整编译,安装所有调试版、发行版的静态库和动态库。1.2.4 定制安装Boost

完整编译Boost费时费力,而且这些库并不可能在开发过程中全部用到,因此,Boost也允许用户自行选择要编译的库。

执行命令:

可查看所有必须编译才能使用的库。

在完全编译命令的基础上,使用-with或者-without选项可打开或者关闭某个库的编译,如:

将仅编译安装date_time库。

本书使用的安装命令是:

b2和bootstrap.sh还有其他很多选项,如指定安装路径、指定debug或release版等,读者可使用-help选项或者参考Boost文档以获得更多信息。1.2.5 验证开发环境

让我们来编写一个简单的Boost应用程序来验证开发环境。

头文件里有两个宏,定义了当前使用的Boost程序库版本号:

头文件里有三个宏:BOOST_PLATFORM、BOOST_COMPILER和BOOST_STDLIB,分别定义了当前的操作系统、编译器和标准库。

下面的代码就是我们与Boost的第一次接触:1.3构建工具

在Linux上有很多C/C++的构建工具,最常用的是make,此外还有cmake、scons等,本书采用的是Boost自带的构建工具b2——boost build v2。它是专门为构建Boost这样复杂度的软件而开发的构建工具,功能强大却又灵活方便,无论什么规模的软件都可以轻松管理——我们已经在安装Boost时见识了它的威力。

本节仅对b2做简略介绍,它的更多功能和使用方式的讲解已经超出了本书的范围。1.3.1 安装b2

为了使用b2构建应用,我们需要在安装Boost程序库后再安装b2程序。

在Boost解压缩后的根目录下执行命令:1.3.2 构建脚本

和make等构建构建工具一样,b2也使用文本格式的构建脚本来管理代码,通常的名字是jamfile或Jamfile。此外b2还有一个特殊的jamroot文件,它需要放在整个项目的根目录下,用于管理项目树,定义整个项目的构建设置。

例如,本书源代码目录结构是:

jamroot文件一般定义整个项目里通用的编译参数、包含路径等设置,避免每个子目录里的jamfile重复定义。对于Linux+GCC来说,常用的配置可以是:1.3.3 构建语言

b2使用的构建语言称为bjam,它是一个解释性语言,拥有完整的语法定义,包括变量、分支、循环语句、函数甚至还有类,如果读者熟悉shell、awk等脚本语言就会发现它们之间有很多相似之处。

完整地讲解bjam语言将耗费大量篇幅,这里仅介绍几个最常用的构建命令,对比make等同类工具可以看到语法非常简洁。

构建目标程序e,使用源码xxx.cpp、yyy.cpp和库zzz:

构建目标t,并且编译后自动执行,通常用于单元测试:

定义链接库zzz,它依赖于depend_libs:

需要特别注意的一点是bjam是基于token的语言,使用空格、tab等空白字符来区分语法元素,不仅是单词,即使是“:”、“;”这样的标点符号前后也必须要有空格,否则bjam会无法识别标点,导致奇怪的语法错误。

下面的语句里“:”、“;”与前面的单词连在了一起,是一个典型的错误例子:1.3.4 构建命令

b2实际上是bjam语言的解释器,它查找当前目录下的jamfile,并向上查找jamroot,解释执行其中的bjam语句,最终完成软件的构建。

常用的b2命令行参数如下:

通常我们直接执行b2就可以完成构建工作,它将构建jamfile里的所有目标,就像make all一样。1.4总结

本章可以说是比赛开始前的“热身运动”,叙述了Boost的历史和特点,然后简要介绍了本书使用的开发环境以及编译安装Boost程序库和构建工具b2,希望读者能够借“他山之石”顺利搭建自己的开发环境。

好了,如果一切准备就绪,接下来就让我们正式开始学习Boost。

[1]随着C++11/14标准的发布,Boost正逐渐向新标准靠拢,趋向于成为一个跨标准的“超级标准库”。

[2]之所以这么做当然是有理由的。首先就是与普通的C头文件(*.h)区分,另一个很重要的原因就是使Boost库不需要预先编译,直接引入程序员的工程即可编译链接,方便了库的使用。Java、C#、PHP、Python程序员应该对这种代码文件形式很熟悉,这几种语言都是在一个文件中编写所有代码。

[3]对于使用其他开发环境的读者只能说抱歉了,作者不能保证书中的脚本或代码能够百分之百正确运行。请参考Boost说明文档查看对您正在使用的平台和编译器的支持情况。

[4]得益于摩尔定律,现在Boost库的编译所需要的时间和空间都已经大大缩减了,在目前主流级别CPU上只需要半小时左右,而在以前则需要数个小时。第2章时间与日期

C++一直以来缺乏对时间和日期的处理能力,而时间和日期又是现实生活中经常遇到的,C++程序员不得不求助于C,使用笨拙的结构和函数。无法忍受这一情形的程序员则手工构造了自己的实现以满足开发所需,可以想象,有无数的程序员在这方面重复了大量的工作。

而现在,Boost使用timer、date_time和chrono完美地解决了这个问题。

本章介绍timer和date_time,而chrono库因为与操作系统联系较密切,将在第10章讲解。2.1timer库概述

timer是一个很小的库,提供简易的度量时间和进度显示功能,可以用于性能测试等需要计时的任务,对于大多数的情况它足够用。

Boost1.48版以后的timer库由两个组件组成:早期的timer(V1)和新的cpu_timer(V2),前者使用的是标准C/C++库函数,而后者则基于chrono库使用操作系统的API,计时精度更高。V1版的timer组件计时精度低,但对于Boost初学者来说还是具有一定的学习价值,故本章介绍这个timer组件,而cpu_timer则放在10.3节介绍。

timer(V1)库包含三个小组件,分别是:计时器timer、progress_timer和进度指示器progress_display,以下将分别详述。2.2timer

timer类可以测量时间的流逝,是一个小型的计时器,提供毫秒级别的计时精度和操作函数,供程序员手工控制使用,就像是个方便的秒表。

timer位于名字空间boost,为了使用timer组件,需要包含头文件,即:2.2.1 用法

让我们通过一段示例代码来看一下如何使用timer。

上面的代码基本说明了timer的接口[1]。timer对象一旦被声明,它的构造函数就启动了计时工作,之后就可以随时用elapsed()函数简单地测量自对象创建后所流逝的时间。成员函数elapsed_min()返回timer测量时间的最小精度,elapsed_max()返回timer能够测量的最大时间范围,两者的单位都是秒。

程序的输出如下:2.2.2 类摘要

timer类非常小,全部实现包括所有注释也不过70余行,真正的实现代码则只有不到20行。作为我们学习的第一个Boost组件,值得把源码全部列出来仔细研究:

timer的计时使用了标准库头文件里的std::clock()函数,它返回自进程启动以来的clock数,每秒的clock数则由宏CLOCKS_PER_SEC定义[2]。

timer的构造函数记录当前的clock数作为计时起点,保存在私有成员变量_start_time中。每当调用elapsed()时就获取此时的clock数,减去计时起点_start_time,再除以CLOCKS_PER_SEC获得以秒为单位的已经流逝的时间。如果调用函数restart(),则重置_start_time重新开始计时。

函数elapsed_min()返回timer能够测量的最小时间单位,是CLOCKS_PER_SEC的倒数。函数elapsed_max()使用了标准库的数值极限类numeric_limits,获得clock_t类型的最大值,采用类似elapsed()的方式计算可能的最大时间范围。

timer没有定义析构函数,这样做是正确且安全的。因为它仅有一个类型为clock_t的成员变量_start_time,故没有必要实现析构函数来特意“释放资源”(也无资源可供释放)。2.2.3 使用建议

timer接口简单,轻巧好用,适用于大部分要求不高的程序计时任务。但使用时我们必须理解elapsed_min()和elapsed_max()这两个计时精度函数的含义,它们表明了timer的能力。

timer不适合高精度的时间测量任务,它的精度依赖于操作系统或编译器,难以做到跨平台。timer也不适合大跨度时间段的测量,如果需要以天、月甚至年作为时间的单位则不能使用timer,应转向10.3节的cpu_timer组件。2.3progress_timer

progress_timer也是一个计时器,它继承自timer,会在析构时自动输出时间,省去了timer手动调用elapsed()的工作,是一个用于自动计时相当方便的小工具。

progress_timer位于名字空间boost,为了使用progress_timer组件,需要包含头文件,即:2.3.1 用法

progress_timer继承了timer的全部能力,可以如timer那样使用,例如:

这样,在程序退出(准确地说是离开main函数局部域)导致progress_timer析构时,会自动输出流逝的时间。显示输出如下:

如果要在一个程序中测量多个时间,可以运用花括号{}限定progress_timer的生命期:

只需要声明progress_timer的实例就可完成所需的全部工作,非常容易。有了progress_timer,程序员今后在做类似性能测试等计算时间的工作时将会感到轻松很多。2.3.2 类摘要

progress_timer的类摘要如下:

progress_timer继承自timer,因此它的接口与timer相同,也很简

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载