<?xml version="1.0" encoding="gb2312"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>导读---STLChina.org 出品</title>
<link href="css/all.css" rel="stylesheet" type="text/css" />
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<style type="text/css">
table
{
width: 90%;
border: 0px;
}
td
{
border: 0px;
}
</style>
</head>
<body>
<h1>导读</h1>
<p>你已经熟悉了STL。你知道怎么建立容器,迭代它们的内容,添加删除元素和应用常见算法,比如find和sort。但你并不满足,你不能摆脱STL所提供的超过它们能带来的好处的感觉。应该简单的任务并非那样。应该直截了当的操作确有资源泄漏或错误行为。应该高效的过程却需要比你希望给它们的更多的时间和内存。是的,你知道怎么使用STL,但你不确定你在<em>有效地</em>使用它。</p>
<p>我为你写了这本书。</p>
<p>在《Effective STL》中,我解释了怎样结合STL组件来在库的设计得到最大的好处。这样的信息允许你对简单、直接的问题开发简单、直接的解决方案,也帮你对更复杂的问题设计优雅的方法。我描述了常见的STL使用错误,而且向你演示怎么避开它们。那帮助你躲开闪资源漏、不可移植的代码和未定义的行为。我讨论了优化代码的方法,所以你能使STL表现得像它应该的那样快速、光滑。</p>
<p>本书里的信息将使你成为一个更好的STL程序员,它将让你成为一个更高产的程序员。而且它将让你成为一个更愉快的程序员,使用STL很有趣,但是有效地使用它更为有趣,这种有趣是它们必须把你拽离键盘,因为你不能相信你争拥有的好时光。即使对STL的匆匆一瞥也能发现它是一个非常酷的库,但这份酷比你可能想象的更宽更深。我在本书的一个主要目标是传达给你这个库有多神奇,因为在我编程的差不多30年里,我从未见过任何像STL的东西。你或许也没有。</p>
<h2>定义、使用和扩展STL</h2>
<p>没有“STL”的官方定义,而且当人们使用这个术语时,不同的人表示的是不同的东西。在本书中,“STL”的意思是与迭代器合作的C++标准库的一部分。那包括标准容器(包括string),iostream库的一部分,函数对象和算法。它不包括标准容器适配器(stack、queue和priority_queue)以及bitset和valarray容器,因为它们缺乏迭代器支持。它也不包括数组。真的,数组以指针的形式支持迭代器,但数组是C++语言的一部分,并非库。</p>
<p>技术上,我的STL的定义排除了标准C++库的扩展,特别是散列容器,单链表,rope和多种非标准函数对象。虽然如此,一个有效的STL程序员需要知道这样的扩展,因此我在合适的地方提到了它们。的确,<a href="item_25.html">条款25</a>致力于非标准散列容器的概述。它们现在不在STL里,但类似它们的东西几乎肯定将要进入标准C++库的下一个版本,而在窥见未来是有价值的。</p>
<p>存在STL扩展的原因之一是STL是被设计为可扩展的库。不过,在本书里,我关注于使用STL,而不是给它添加新的组件。例如,如果你发现,我没有说多少关于写你自己的算法的东西,而且我根本没有在写新容器和迭代器上提供指导。我相信在你着手增加它的能力之前,掌握STL已经提供的东西很重要,所以那是我在《Effective STL》里关注的。当你决定建立你自己STLesque组件时,你将在像Josuttis的《The C++ Standard Library》<a href="bibliography.html#bi03">[3]</a>和Austern的《Generic Programming and the STL》<a href="bibliography.html#bi04">[4]</a>这样的书里找到建议。我确实在这本书里讨论的STL扩展的一个方面是写你自己的函数对象。你不可能在不知道怎么写自己的函数对象的情况下有效地使用STL,所以我为这个主题投入了整整一章(<a href="chap_06.html">第6章</a>)。</p>
<h2>引用</h2>
<p>先前段落中对Josuttis和Austern的书的引用演示了我怎么处理书目引用的。通常,我试图提及引用的足够信息以便已经熟悉它的人能够鉴别出来。例如,如果你已经知道这些作者的书,就不必转向<a href="bibliography.html">参考书目</a>那里查明<a href="bibliography.html#bi03">[3]</a>和<a href="bibliography.html#bi04">[4]</a>提及的你已经知道的书。当然,如果你不熟悉一个出版物,<a href="bibliography.html">参考书目</a>(从第225页开始)会给你全面的引用。</p>
<p>我经常引用三个一般没有使用引用编号的东西。第一个是C++国际标准<a href="bibliography.html#bi05">[5]</a>,我通常把它简称为“标准”。其他两个是我早先关于C++的书,《Effective C++》<a href="bibliography.html#bi01">[1]</a>和《More Effective C++》<a href="bibliography.html#bi02">[2]</a>。</p>
<h2>STL和标准</h2>
<p>我经常提及C++标准,因为《Effective STL》专注于可移植的,与标准一致的C++。理论上,我在这本书里演示的一切都可以用于每个C++实现。实际上,那不是真的。编译器的缺陷和STL实现凑合成防止一些有效的代码编译或表现出它们应该有的行为。那是很常见的情况,我描述了这些问题,而且解释了你应该怎么变通地解决他们。</p>
<p>有时候,最容易的变通办法是使用另一个STL实现。<a href="appendix_b.html">附录B</a>给一个这种情况的例子。实际上,STL用得越多,<em>编译器</em>和<em>库实现</em>的区别就越重要。程序员在设法让合法的代码编译时遇到困难,他们通常责备他们的编译器,但对于STL,编译器可能是好的,而STL实现是不良的。为了强调你得依赖编译器和库实现的事实,我使用你的<dfn>STL平台</dfn>。一个STL平台是一个特定编译器和一个标准模板库特定实现的组合。在本书里,如果我提及一个编译器问题,你能确信我意思是编译器有问题。但是,如果我说你的STL平台有问题,你应该理解为“可能是编译器缺陷,可能是库缺陷,或许都有”。</p>
<p>我一般提及你的“编译器们”——<em>复数</em>。那是我长期相信你通过确保代码可以在多于一个的编译器上工作的方法来改进你的代码质量(特别是移植性)的产物。此外,使用多个编译器一般可以简化拆解由STL的使用不当造成的错误信息难题。(<a href="item_49.html">条款49</a>致力于解码此类消息的方法。)</p>
<p>关于与标准一致的代码,我强调的另一个方面是你应该避免构造未定义行为。这样的构造可能在运行期做任何事情。不幸的是,这意味着它们可能正好做了你想要的,而那会导致一种错误的安全感。太多程序员以为未定义行为总会导致一个明显的问题,例如,一个分段错误或其他灾难性的错误。未定义行为的结果实际上更为狡猾,例如,破坏极少引用的数据。它们也可以通过程序运行。我发现一个未定义行为的好定义是“为我工作,为你工作,在开发和QA期间工作,但在你最重要的用户面前爆炸了”。避免未定义行为很重要,所以我指出了它会出现的通常情况。你应该训练你自己警惕这样的情况。</p>
<h2>引用计数</h2>
<p>讨论STL而没有提及引用计数是几乎不可能的。正如你将在<a href="item_07.html">条款7</a>和<a href="item_33.html">33</a>看见的,基于指针的容器的设计几乎总要导致引用计数。另外, 很多string实现内部是引用计数的,而且,正如<a href="item_15.html">条款15</a>解释的,这可能是一个你不能忽视的实现细节。在本书里,我认为你已经熟悉引用的基础。如果你不是,大多数中级和高级C++教材都覆盖了这个主题。例如,在《More Effective C++》里,相关材料是在条款28和29。如果你不知道引用计数是什么而且你不喜欢学习,不要担心。你也可以读完本书,虽然这里那里可能有一些句子不像它们应该的那么有意义。</p>
<h2><code>string</code>和<code>wstring</code></h2>
<p>我关于string说的任何东西都可以相等地应用到给它的宽字符兄弟,wstring。类似地,任何时候我提及string和char或char*之间的关系,对于wstring和wchar_t或wchar_t*之间的关序也是正确的。换句话说,只是因为我在本书里不明确地提及宽字符字符串,不要以为STL不能支持它们。它也像基于char的字符串一样支持它们。它必须。string和wstring都是同一个模板的实例化,basic_string。</p>
<h2>术语,术语,术语</h2>
<p>这不是STL的入门书,所以我认为你知道基本的东西。仍然,下面的术语十分重要,我感到需要强迫复习它们:</p>
<ul>
<li>vector、string、deque和list被称为<dfn>标准序列容器</dfn>。<dfn>标准关联容器</dfn>被是set、multiset、map和multimap。</li>
<li>迭代器被分成五个种类,基于它们支持的操作。简要地说,<dfn>输入迭代器</dfn>是每个迭代位置只能被读一次的只读迭代器。<dfn>输出迭代器</dfn>是每个迭代位置只能被写一次的只写迭代器。输入和输出迭代器被塑造为读和写输入和输出流(例如,文件)。因此并不奇怪输入和输出迭代器最通常的表现分别是istream_iterator和ostream_iterator。<br />
<p><dfn>前向迭代器</dfn>有输入和输出迭代器的能力,但是它们可以反复读或写一个位置。它们不支持operator--,所以它们可以高效地向前移动任意次数。所有标准STL容器都支持比前向迭代器更强大的迭代器,但是,正如你可以在<a href="item_25.html">条款25</a>看到的,散列容器的一种设计可以产生前向迭代器。单链表容器(在<a href="item_50.html">条款50</a>提到)也提供前向迭代器。</p>
<p><dfn>双向迭代器</dfn>就像前向迭代器,除了它们的后退可以像前进一样容易。标准关联容器都提供双向迭代器。list也有。</p>
<p><dfn>随机访问迭代器</dfn>可以做双向迭代器做的一切事情,但它们也提供“迭代器算术”,即,有一步向前或向后跳的能力。vector、string和deque都提供随机访问迭代器。指进数组的指针可以作为数组的随机访问迭代器。</p>
</li>
<li>重载了函数调用操作符(即,operator())的任何类叫做<dfn>仿函�