免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: haoji

《Effective STL》 [复制链接]

论坛徽章:
0
发表于 2008-05-31 17:11 |显示全部楼层
for (int i = CHAR_MIN; i <= CHAR_MAX; ++i)
tab[i - CHAR_MIN] = (char) i;
ct.toupper(tab, tab + (CHAR_MAX - CHAR_MIN + 1));
}
bool operator()(const std::string& x, const std::string& y) const {
return std::lexicographical_compare(x.begin(), x.end(),
y.begin(), y.end(),
lt_char(tab));
}
};
正如你看见的,lt_str_1和lt_str_2不是非常不同。前者有一个直接使用ctype方面的字符比较函数对象,而后者
使用一张预先算好的大写转换表的字符比较函数对象。如果你建立lt_str_2函数对象,使用它比较一些短字符
串,然后放弃它,可能会比较慢。不过,对任何实际的使用来说,lt_str_2将明显比lt_str_1快。在我的系统上
差别不止两倍:用lt_str_1排序一个23,791个单词的list花费0.86秒,而用lt_str_2花费0.4秒。
我们从所有这些里学到了什么?
● 忽略大小写的字符串类是错误的抽象层面。C++标准库中的泛型算法是由策略参数化的,而你应该利
用这个事实。
● 词典字符串比较建立在字符比较之上。一旦你有了一个忽略大小写的字符比较函数对象,问题就解决
了。(而且你可以把那个函数对象重用于比较其他类型的字符序列,比如vector<char>,或字符串
表,或原始的C字符串。)
● 忽略大小写的字符比较比看起来难。除了在一个特定区域设置的场景之外,它没有意义,所以字符比
较函数对象需要储存区域设置信息。如果关系到速度,你应该写避免重复调用昂贵的方面操作的函数
对象。
正确的忽略大小写比较花费了大量手段,但是你只须把它写一次。你或许不想考虑locale;大多数人不。(谁
想在1990年考虑千年虫?)如果你依赖区域设置的代码正确了,那么你忽视区域设置的可能性将大于你写出
消除了这个依赖性的代码。
[1] 参见Andrei Alexandrescu在《C++ Report》2000年4月的专栏[19]。 [2] 警告:use_facet是一个函数模板,它的
模板参数值出现在返回类型,而不是任何实参。使用一个叫做显式模板参数特化的语言特性来调用它,而一
些C++编译器尚未支持那个特性。如果你使用了一个不支持的编译器,你的库实现可能提供了变通办法,所
以你可以用某种方式调用use_facet。

论坛徽章:
0
发表于 2008-05-31 17:12 |显示全部楼层
在微软STL平台上的注意事项
在这本书的开头几页里,我提到了术语STL平台是指一个特定编译器和一个标准模板库特定实现的组合。如
果你在使用版本6或更早的Microsoft Visual C++编译器(即,伴随版本6或更早的Microsoft Visual Studio的编译
器),在编译器和库之间的区别特别重要,因为编译器有时比伴随的STL实现更有能力。在本附录中,我描
述了旧的微软STL平台的一个重要缺点,而且我提供可以明显改进你STL经验的变通办法。
下面是给使用Microsoft Visual C++(MSVC)4-6版的开发者的信息。如果你正使用Visual C++ .NET,你的STL
平台没有下面描述的那些问题,你可以略过这个附录。
STL里的成员函数模板
假设你有两个Widget的vector,你想把一个vector里的Widget拷贝到另一个的末端。那很容易。只要使用vector
的区间insert函数(参见条款5):
vector<Widget> vw1, vw2;
...
vw1.insert(vw1.end(), vw2.begin(), vw2.end()); // 把vw2里Widget的副本
// 追加到vw1
如果你有一个vector和一个deque,你一样可以这么做:
vector<Widget> vw;
deque<Widget> dw;
vw.insert(vw.end(), dw.begin(), dw.end()); // 把dw里Widget的副本
// 追加到vw
实际上,你不必理会被拷贝的容器容纳的是什么类型的对象。即使自定义容器也可以工作:
vector<Widget> vw;
...
list <Widget> lw;
...
vw.insert(vw.begin(), lw.begin(), lw.end()); // 把lw里Widget的副本

论坛徽章:
0
发表于 2008-05-31 17:12 |显示全部楼层
// 追加到vw
set<Widget> sw;
...
vw.insert(vw.begin(), sw.begin(), sw.end()); // 把sw里Widget的副本
// 追加到vw
template<typename T, // 用于自定义
typename Allocator = allocator<T> > // 兼容STL的
class SpecialContainer { ... }; // 容器模板
SpecialContainer<Widget> scw;
...
vw.insert(vw.end(), scw.begin(), scw.end()); // 把scw里Widget的副本
// 追加到vw
这种灵活性是可能的,因为vector的区间insert函数完全不是一个函数。相反,它是一个成员函数模板,可以
用任何迭代器类型实例化,以产生一个具体的区间insert函数。对于vector,标准像这样声明insert模板:
template <class T, class Allocator = allocator<T> >
class vector {
public:
...
template <class InputIterator>
void insert(iterator position, InputIterator first, InputIterator last);
...
};
每个标准容器都要求提供这个模板化的区间insert。容器也要求提供类似成员函数模板的区间构造函数和
assign的区间形式(两者都在条款5讨论)。
MSVC版本4-6
不幸的是,伴随MSVC版本4-6的STL实现没有声明成员函数模板。这个库最初为MSVC版本4开发,而那个编
译器,像它所在时代的大多数编译器一样,缺乏成员函数模板的能力。在MSVC4到MSVC6之间,编译器增
加了对这些模板的支持,但是,由于法律诉讼的影响,微软没有直接包含它们,库基本保持冻结。
因为伴随MSVC4-6的STL实现是为一个缺乏成员函数模板的编译器设计的,库的作者通过用具体函数替换每
个模板的方法来近似这样的功能性,也就是只接受和容器的迭代器类型相同的迭代器。例如,对于insert,这

论坛徽章:
0
发表于 2008-05-31 17:13 |显示全部楼层
个成员函数模板被替换这样:
void insert(iterator position, // “iterator”是
iterator first, iterator last); // 容器的迭代器类型
受限的区间成员函数形式可以进行从一个vector<Widget>到一个vector<Widget>或从一个list<int>到一个
list<int>的区间插入,但不能从一个vector<Widget>到一个list<Widget>或从一个set<int>到一个deque<int>。甚
至不可能进行从一个vector<long>到一个vector<int>的区间insert(或assign或构造),因为vector<long>::iterator
与vector<int>::iterator类型不同。结果,下面十分有效的代码不能用MSVC4-6编译:
istream iterator<Widget> begin(cin), end; // 建立用于从cin
// 读取Widget的
// begin和end迭代器
// (参见条款6)
vector<Widget> vw(begin, end); // 把cin的Widget读入vw
// (再次参见条款6);在MSVC4-6
// 不能编译
list<Widget> lw;
...
lw.assign(vw.rbegin(), vw.rend()); // 把vw的内容赋值给lw
// (以反序);在MSVC4-6
// 不能编译
SpecialContainer<Widget> scw;
...
scw.insert(scw.end(), lw.begin(), lw.end()); // 把lw中的Widget的副本
// 插入scw的末端;
// 在MSVC4-6不能编译
那如果你必须使用MSVC4-6,你该怎么办?那取决于你使用的MSVC版本和是否你被迫使用伴随编译器的
STL实现。
MSVC4-5的变通办法
再次看看不能用伴随MSVC4-6的STL编译的有效代码例子:
vector<Widget> vw(begin, end); // 被MSVC4-6的

论坛徽章:
0
发表于 2008-05-31 17:13 |显示全部楼层
// STL实现拒绝
list<Widget> lw;
...
lw.assign(vw.rbegin(), vw.rend()); // 也拒绝
SpecialContainer<Widget> scw;
...
scw.insert(scw.end(), lw.begin(), lw.end()); // 同上
这些调用看起来相当不同,但它们全都由于相同的原因而失败:在STL实现里缺乏成员函数模板。对它们有
一种单独的治疗方法:使用copy和插入迭代器(参见条款30)。例如,这里是上面例子的变通办法:
istream_iterator<Widget> begin(cin), end;
vector<Widget> vw; // 默认构造vw;
copy(begin, end, back_inserter(vw)); // 然后把cin中的
// Widget拷贝进去
list<Widget> lw;
...
lw.clear(); // 去除lw的老
copy(vw.rbegin(), vw.rend(), back_inserter(lw)); // Widget;把
// vw的Widget拷贝进去(以
// 反序)
SpecialContainer<Widget> scw;
...
copy(lw.begin(), lw.end(), // 把lw的Widget拷贝到
inserter(scw, scw.end())); // scw的结尾
我鼓励你在伴随MSVC4-5的库上使用这样的基于copy的变通办法,但是注意!不要满足于这个变通办法,你
忘记了它们只是变通办法。正如条款5解释的,使用copy算法几乎总是不如使用一个区间成员函数,所以一
旦你有机会把你的STL平台升级到支持成员函数模板的版本,就在区间成员函数是正确方法的地方停止使用
copy。
用于MSVC6的另一个变通办法
你也可以对MSVC6使用MSVC4-5的变通办法,但对于MSVC6有另一个选择。作为MSVC4-5一部分的编译器
没有提供有意义的成员函数模板,所以STL实现缺乏它们的事实是无关紧要的。MSVC6的形势则不同,因为

论坛徽章:
0
发表于 2008-05-31 17:14 |显示全部楼层
MSVC6的编译器支持成员函数模板。因此有理由考虑用提供标准指定的成员函数模板的STL实现替换伴随
MSVC6的。
条款50解释了SGI和STLport都提供了可以自由下载的STL实现,而且那两个实现都把MSVC6编译器作为将配
合的编译器之一。你也可以从Dinkumware购买最新的兼容MSVC的STL实现。每种选择各有利弊。
SGI的和STLport的实现是自由的,我想你知道那在对软件的官方支持上代表什么:完全没有。而且,因为SGI
和STLport把他们的库设计为使用多种编译器,你或许必须手工配置它们的实现来最有效地使用MSVC6。特
别是,你可能必须明确地启用成员函数模板的支持,因为,它们要使用很多编译器,SGI和/或STLport默认可
能不启用它。你可能也得为与其他MSVC6库(特别是DLL)链接而担心,包括保证你使用合适的线程和调试
构建,等等。
如果你被那些事吓着了,或如果你听过你负担不起自由软件的牢骚,你可能要看看Dinkumware用于MSVC6
的替代库。它被设计为提高原生MSVC6 STL的兼容性,并使作为STL平台的MSVC6对标准的支持最大化。因
为Dinkumware写了伴随MSVC6的STL,所以他们最新的STL实现有很大的可能性真的是一个合适的替代品。
要了解更多关于Dinkumware STL实现的信息,访问公司的网站:http://www.dinkumware.com/。
不管你选择的是SGI的、STLport的还是Dinkumware的实现作为STL的替代品,你将得到的不只是带有成员函
数模板的STL。你也将在库的其他地方旁路一致性问题,比如没有声明push_back的string。此外,你可以访问
有用的STL扩展,包括散列容器(参见条款25)和单链表(slists)。SGI的和STLport的实现也提供了多种非标
准的仿函数类,比如select1st和select2nd(参见条款50)。
即使你被伴随MSVC6的STL实现困住,访问Dinkumware网站或许也是值得的。那个网站列举了在MSVC6的库
实现里的已知漏洞并解释怎样修改你的库副本来减少它的缺陷。不用说,编辑你的库头文件是让你自己冒险
的事。如果你遇到麻烦,不要责备我。

论坛徽章:
0
发表于 2008-05-31 17:14 |显示全部楼层
Center of STL Study
——最优秀的STL学习网站
词汇表
本表列出的是一些单词在本书中使用的翻译。这些单词大部分是专业术语,一部分是字典上没有的。
英文 中文
adapter 适配器
algorithm 算法
allocate 分配
allocator 分配器
amortize 分摊
argument 实参
associative container 关联容器
cast 映射
category 种类
component 组件
context 场景
constness 常数性
container 容器
deallocate 回收
dereference 解引用
diagnostics 诊断信息
encapsulation 封装
equality 相等
equivalence 等价
exception safey 异常安全
function object 函数对象
functionality 功能性
functor 仿函数
generalize 泛化

论坛徽章:
0
发表于 2008-05-31 17:15 |显示全部楼层
hash 散列
implementation 实现
instantiation 实例化
iterator 迭代器
key 键
lexical 词法
locale 区域设置
namespace 名字空间
pivot element 主元
parameter 参数
predicate 判断式
range 区间
reference counting 引用计数
roll back 回退
scope 作用域
semantics 语义
sequence container 序列容器
slice 分割
smart pointer 智能指针
specification 特化
splice 接合
trait 特性
transactional 事务性
workaround 变通办法

论坛徽章:
0
发表于 2008-05-31 17:16 |显示全部楼层
关于本电子书
制作本书的目的是为了方便大家的阅读。转载时请保持本电子书的完整性。
前言、条款2、16、21、44根据从Addison-Wesley出版社下载的开放条款翻译。条款26、27、28、45根据从
Scott Meyers的网站下载的《Three Guidelines for Effective Iterator Usage》一文翻译。条款43根据从C/C++ Users
Journal网站下载的《STL Algorithms vs. Hand-Written Loops》一文翻译,条款45根据从C/C++ Users Journal网站
下载的《Distinguishing STL Search Algorithms》一文翻译。其余部分根据epubcn放出的电子书制作。
翻译制作:龚敏敏 2004/1/2
游戏引擎开发网
PDF制作: 抚琴舞剑 2005/4/26
由于目录用到了javascript,所以在WinXP SP2安装后会出现所谓的“安全保护”,这是只需选择“允许阻止的
内容...”即可。强烈推荐使用FireFox 0.9以上观看,从开放源代码、技术含量、安全性、速度、对标准的支持
度等角度看,FireFox都远远优于IE。而且将会遇到麻烦还少一些。

论坛徽章:
0
发表于 2008-06-05 17:40 |显示全部楼层
明天我也贴本书。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP