读书笔记 effective c++ Item 54 让你自己熟悉包括TR1在内的标准库

1. C++0x的历史渊源

C++标准——也就是定义语言的文档和程序库——在1998被批准。在2003年,一个小的“修复bug”版本被发布。然而标准委员会仍然在继续他们的工作,一个“2.0版本”的C++标准预计在2009年被发布(虽然所有的工作很有可能在2007年底被完成)。直到现在,发布下一版C++的预计年份还没有被确定,这就解释了为什么人们把下一版C++叫做“C++0x”——C++的200x年版本。

C++0x可能会包含一些有趣的新的语言特性,但是大多数新C++功能将会以标准库附加物的形式被发布。我们已经知道了一些新的库功能将会是什么,因为它们已经在文档TR1(来自C++库工作组的Technical Report 1)中被指定了。在C++0x被官方正式发布之前,标准委员会保有对TR1的功能进行修改的权利,但是不太可能有大的修改。TR1预示了一个新的C++ release的开始——我们可能将其叫做标准C++ 1.1.如果你不熟悉TR1的功能,你不能被称作一个effective C++程序员,因为TR1中的功能对于各个种类的库和应用来说都是一种福利。

2. C++98标准库中都有什么?

在考察什么是TR1之前,回顾一下C++98版本标准库中的主要部分是很有价值的:

  • 标准模版库(STL),包含容器(vector,string,,map等等);迭代器,算法(find,sort,transform等等);函数对象(less,greater等等);还有不同的容器和函数对象适配器(stack,priority_queue,mem_fun,not1等)
  • Iostreams,包括对用户自定义buffering的支持,国际化IO,和预定义对象cin,cout,cerr和clog.
  • 支持国际化,包括多区域的能力(multipue active locales)。像类型wchar_t(通常是16bits/char)和wstring(wchar_t组成的string)能够促进同Unicode一块工作。
  • 支持对数值的处理,包括复杂数(complex)模板和纯数数组(valarray)。
  • 异常继承体系,包括基类异常,派生类logic_error和runtime_error,还有继承自这些类的其他类。
  • C89的标准库。在1989 C标准库中的所有东西同样被放入了C++。

如果你对上面的任何条款不熟悉,我建议你抽出足够的时间来看一些c++参考读物。

3. TR1中都包含什么?

TR1提出了14个新的组件(也就是程序库功能片段(pieces))。所有都被放入std命名空间中,更精确的说,是在内嵌命名空间tr1中。TR1组件 shared_ptr的全称因此就为std::tr1::shared_ptr。在这本书中,当讨论标准库的组件时,我通常会省略std::,但是我总是会为TR1组件加上前缀tr1::。

本书举例说明TR1中的一些组件:

  • 智能指针 tr1::shared_ptr和tr1::weak_ptr。Tr1::shared_ptr的行为表现就像内建指针一样,但是它们追踪了有多少个tr1::shared_ptr指针指向一个对象。这被叫做引用计数。当最后的指针被销毁(也就是对象的引用计数变为0的时候),对象自动被delete。这在非环状数据结构中用于防止资源泄漏很好,但如果两个或者多个对象包含tr1::shared_ptr,这样一个环就形成了,这个循环可能相互持有对方对象的引用计数,而且都大于0——即使当所有的环的外部指针被销毁了(也就是当作为一个整体的对象组不能被使用了)。这时候就得使用tr1::weak_ptr,tr1::weak_ptr被设计为在非环状tr1::shared_base数据结构中的cycle-inducing 指针。Tr1::weak_ptr中没有引用计数。当指向对象的最后一个shared_ptr被销毁时,对象就会被delete,即使tr1::weak_ptr仍然指向这个对象。然而这样的指针会被自动被标记为失效。

Tr1::shared_ptr可能是TR1中最被广泛使用的对象。我在这本书中也使用了多次,包括在Item 13中,在这个条款中我解释了为什么它如此重要。(这本书没有weak_ptr的使用)

  • Tr1::function,使用它可以表示任意可调用实体(例如,任何函数或者函数对象),只要这些实体的签名同目标签名是一致的。如果我们想使用它来注册一个回调函数,这个函数用int作为参数并且返回值为string。我们可以这么做:
1 void registerCallback(std::string func(int));   // param type is a function
2 // taking an int and
3 // returning a string

参数名字 func是可选的,所以registerCallback可以被声明为如下:

1 void registerCallback(std::string (int));   // same as above; param
2 // name is omitted

注意在这里“std::string(int)”为函数签名。Tr1:;function可以使registerCallback更加灵活,它可以接受任何可调用实体作为它的参数,这个调用实体使用int或者可以转换为Int的任何东西作为参数,返回值可以为一个string或者可以转换为string的任何东西。Tr1::function使用目标函数签名作为模板参数:

1 void registerCallback(std::tr1::function<std::string (int)> func);
2 // the param “func” will
3 // take any callable entity
4 // with a sig consistent
5 // with “std::string (int)”

这种灵活性非常有用,我已经在Item 35中展示过了。

  • Tr1::bind,它能做STL绑定器bind1st和bind2nd能做的所有事情,甚至更多。不像pre-TR1中的binders,tr1::bind可以工作在const和非const成员函数中;可以使用按引用传递的参数;可以在没有其他函数帮助的情况下处理函数指针,所以在调用tr1::bind之前就没有必要同ptr_fun,mem_fun或者mem_fun_ref掺杂在一起了。简单说,tr1::bind是第二代绑定工具,它要远远好于第一代。我已经在Item 35中进行了举例。

我将剩下的TR1组件分成两部分。第一部分提供了相当独立的功能:

  • Hash table 被用来实现set,multiset,map和multimap。每个新的容器都将模拟与pre-TR1相对应部分的接口。对于TR1中的hash table,最让人感到意外的是它们的名字:tr1::unordered_set,tr1::unordered_multiset,tr1::unordered_map和tr1::unordered_multimap。这些名字强调了它的内容不会像set,multiset或者multimap一样,TR1中hash-based的容器中的元素顺序是无序的。
  • 正则表达式,包括在字符串上进行的基于正则表达式的搜索和替换功能,还有从一个匹配字符串到另一个匹配字符串的迭代等等。
  • Tuple,它是对已经在标准库中存在的pair模板的泛化。相比于Pair对象会持有两个对象,tr1::tuple对象能够持有任意数量的对象。
  • Tr1::array,本质上来说是一个“STL化的“数组,也就是一个支持像begin和end这样的成员函数的数组。Tr1::array的大小在编译期被固定;对象不使用动态内存。
  • Tr1::mem_fn,为成员函数指针进行适配的在句法上的一个统一的方式。就像tr1::bind把C++98的bind1st和bind2nd的功能包含进来并对其进行扩展,tr1::mem_fn把C++98中的mem_fun和mem_fun_ref的功能包含进来并对其进行了扩展。
  • Tr1::reference_wrapper,这是一个功能使得引用的行为表现就像对象一样。这使得创建一个行为表现就如同持有引用的容器成为可能(事实上,容器只能包含对象或者指针。)
  • 随机数生成器(Random number generation)功能要比从C标准库中继承而来的随机函数更加优秀。
  • 数学特殊函数(Mathematical special function),包括拉盖尔多项式,贝塞尔函数,完全椭圆积分(complete elliptic integrals)等等。
  • C99兼容性扩展,为了将许多新的C99程序库的功能引入到C++中而设计的函数集合与模板。

TR1组件的第二个集合由为更加复杂的模板编程技术提供的支撑技术所组成,包括模板元编程(Item 48):

  • 类型特性(Type traits),提供了一系列trait类(见Item 47)来为类型提供编译时信息。给定一个类型T,TR1的类型特性能够揭示T是否是一个内建类型,能否提供虚析构函数,是否是一个empty class(Item 39),是否可以隐式的转换为其它类型U,等等。TR1中的type traits同样也能够为一个类型揭示合适的对齐问题(alignment),这就为实现自定义内存分配函数的程序员提供了重要信息(Item 50)。
  • Tr1::result_of,一个用来推导函数返回类型的模板。当实现模板的时候,能够引用从函数(模板)调用中返回回来的对象类型很重要,但是返回类型可以以复杂的方式来依赖函数的参数。在TR1中很多地方都使用到了Tr1::result_of。

虽然TR1中的一些功能(尤其是tr1::bind和tr1::mem_fn)只是将pre-TR1的一些组件纳入其中,但TR1只是标准库的额外添加物。没有TR1组件是对现存组件的替换,所以使用pre-TR1构建的遗留代码仍然有效。

4. 从哪里找到TR1实现

TR1本身只是一个文档。为了使用它指定的功能,你需要访问实现这些功能的代码。这些代码最后将会同编译器捆绑在一块发布,但是我写这本书是在2005年,如果在你的标准库实现中寻找TR1组件,可能会有一些遗漏。幸运的是,可以从其他地方进行搜寻:TR1 的14个组件中的10个是基于可以免费获得的Boost库(见Item 55)来实现的,所以如果你想了解和TR1类似(TR1-like)的功能,这会是一个很好的资源。这里我说“TR1-like”,因为虽然很多TR1功能是基于Boost库的,有一些地方Boost功能还没有同TR1规格完全匹配。很有可能但你读到这本书的时候,不仅对于从Boost 库进化而来的TR1组件,Boost中有了与其一致的实现,而且它同时提供了没有基于Boost的其余4个TR1组件的实现。

如果作为权宜之计你想使用Boost中的类似TR1的库,直到编译器同TR1实现一同被发布,你可能会使用一个命名空间的技俩。所有的Boost组件是在命名空间boost中,但是TR1组件将会在命名空间std::tr1中。你可以告诉编译器,把对std::tr1的引用当作对boost的引用来处理。像下面这样:

1 namespace std {
2 namespace tr1 = ::boost; // namespace std::tr1 is an alias
3 } // for namespace boost

从技术上来说,这会让你进入未定义行为的领域,因为正如在Item 25中解释的,不允许向std命名空间中添加任何东西。在实际情况下,看上去你不会遇到任何麻烦。当你的编译器提供了它们自己的TR1实现的时候,所有你需要做的就是移除上面的命名空间别名;引用std::tr1的代码仍然是有效的。

可能没有基于Boost库实现的TR1中的最重要的部分就是hash table了,但是hash  tables已经存在很多年了,它们以hash_set,hash_multiset,hasp_map和hash_multimap命名。有可能你的编译器自带的库中已经包含这些模板了。如果没有,使用你最喜欢的搜索引擎去搜一下这些名字,因为你肯定能够找到一些源代码,无论是商业的还是免费的。

5. 总结

  • 主要的标准C++库的功能包括STL,iostream和locales。C89标准库也被包含在内。
  • TR1中添加了对智能指针的支持,广义的函数指针(tr1::function),hash-based 容器,正则表达式和10个其他的组件。
  • TR1本身只是一个说明书。为了使用TR1,你需要一份实现。TR1组件实现的一份源码来自于Boost.
时间: 2024-10-08 13:43:14

读书笔记 effective c++ Item 54 让你自己熟悉包括TR1在内的标准库的相关文章

读书笔记 effective c++ Item 55 让你自己熟悉Boost

你正在寻找一个高质量的,开源的,与平台和编译器无关的程序库的集合?看一下Boost吧.想加入一个由雄心勃勃的,充满天赋的正致力于最高水平的程序库设计和实现工作的C++程序员们组成的团体么?看一下Boost吧.想了解C++将来可能会是什么样子的?看一下Boost吧. Boost是一个C++开发人员组成的团体,也是供免费下载的C++程序库的集合.网址是http://boost.org. 1. Boost的两大优势 当然,有许多C++组织和网站,但是Boost有两点是其它组织不能与之媲美的.首先,它和

读书笔记 effective c++ Item 47 使用traits class表示类型信息

STL主要由为容器,迭代器和算法创建的模板组成,但是也有一些功能模板.其中之一叫做advance.Advance将一个指定的迭代器移动指定的距离: 1 template<typename IterT, typename DistT> // move iter d units 2 void advance(IterT& iter, DistT d); // forward; if d < 0, 3 // move iter backward 从概念上来说,advance仅仅做了it

读书笔记 effective c++ Item 45 使用成员函数模板来接受“所有兼容类型”

智能指针的行为像是指针,但是没有提供加的功能.例如,Item 13中解释了如何使用标准auto_ptr和tr1::shared_ptr指针在正确的时间自动删除堆上的资源.STL容器中的迭代器基本上都是智能指针:当然,你不能通过使用“++”来将链表中的指向一个节点的内建指针移到下一个节点上去,但是list::iterator可以这么做. 1. 问题分析——如何实现智能指针的隐式转换 真正的指针能够做好的一件事情是支持隐式转换.派生类指针可以隐式转换为基类指针,指向非const的指针可以隐式转换成为

读书笔记 effective c++ Item 26 尽量推迟变量的定义

1. 定义变量会引发构造和析构开销 每当你定义一种类型的变量时:当控制流到达变量的定义点时,你引入了调用构造函数的开销,当离开变量的作用域之后,你引入了调用析构函数的开销.对未使用到的变量同样会产生开销,因此对这种定义要尽可能的避免. 2. 普通函数中的变量定义推迟 2.1 变量有可能不会被使用到的例子 你可能会想你永远不会定义未使用的变量,你可能要再考虑考虑.看下面的函数,此函数返回password的加密版本,提供的password需要足够长.如果password太短,函数会抛出一个logic

读书笔记 effective c++ Item 13 用对象来管理资源

1.不要手动释放从函数返回的堆资源 假设你正在处理一个模拟Investment的程序库,不同的Investmetn类型从Investment基类继承而来, 1 class Investment { ... }; // root class of hierarchy of 2 3 // investment types 进一步假设这个程序库通过一个工厂函数(Item 7)来给我们提供特定Investment对象: 1 Investment* createInvestment(); // retur

读书笔记 effective c++ Item 48 了解模板元编程

1. TMP是什么? 模板元编程(template metaprogramming TMP)是实现基于模板的C++程序的过程,它能够在编译期执行.你可以想一想:一个模板元程序是用C++实现的并且可以在C++编译器内部运行的一个程序,它的输出——从模板中实例化出来的C++源码片段——会像往常一样被编译. 2. 使用TMP的优势 如果这没有冲击到你,是因为你没有足够尽力去想. C++不是为了模板元编程而设计的,但是自从TMP早在1990年被发现之后,它就被证明是非常有用的,为了使TMP的使用更加容易

读书笔记 effective c++ Item 38 通过组合(composition)为 “has-a”或者“is-implemented-in-terms-of”建模

1. 什么是组合(composition)? 组合(composition)是一种类型之间的关系,这种关系当一种类型的对象包含另外一种类型的对象时就会产生.举个例子: 1 class Address { ... }; // where someone lives 2 3 class PhoneNumber { ... }; 4 class Person { 5 public: 6 ... 7 private: 8 std::string name; // composed object 9 10

读书笔记 effective c++ Item 52 如果你实现了placement new,你也要实现placement delete

1. 调用普通版本的operator new抛出异常会发生什么? Placement new和placement delete不是C++动物园中最常遇到的猛兽,所以你不用担心你对它们不熟悉.当你像下面这样实现一个new表达式的时候,回忆一下Item 16和Item 17: 1 Widget *pw = new Widget; 两个函数会被调用:一个是调用operator new来分配内存,第二个是Widget的默认构造函数. 假设第一个调用成功了,但是调用第二个函数抛出了异常.在这种情况下,对步

读书笔记 effective c++ Item 35 考虑虚函数的替代者

1. 突破思维——不要将思维限定在面向对象方法上 你正在制作一个视频游戏,你正在为游戏中的人物设计一个类继承体系.你的游戏处在农耕时代,人类很容易受伤或者说健康度降低.因此你决定为其提供一个成员函数,healthValue,返回一个整型值来表明一个人物的健康度.因为不同的人物会用不同的方式来计算健康度,将healthValue声明为虚函数看上去是一个比较明显的设计方式: 1 class GameCharacter { 2 public: 3 4 virtual int healthValue()