《Effective STL》读书摘要

基本信息

  • 作者:Scott Meyers(Effective C++系列作者)
  • 来源:云盘 /田浩然上传的资料/电子书/C_C++/[计算机科学经典着作].Addison.Wesley.Effective.Stl.50.Specific.Ways.To.Improve.Your.Use.Of.Stl.pdf
  • 页数:198页
  • 年份:2001年
  • 类别:编程书籍 / C++ / STL最佳实践

核心框架

本书以50条Item的形式,逐一讲解STL使用中常见的陷阱和最佳实践。每条Item结构为:问题 → 解释 → 解决方案 → 代码示例。

全书按主题分为6章:

  1. Containers(容器通用)
  2. vector and string
  3. Associative Containers(关联容器)
  4. Iterators
  5. Algorithms
  6. Functors、Function Objects等
  7. Programming with the STL

核心Item精要

容器通用(Items 1-12)

Item要点
1谨慎选择容器:区分连续内存容器(vector/deque/string)与节点容器(list/set/map)。考虑:插入位置、排序需求、标准合规、迭代器类型、元素移动、内存兼容性、查找速度、引用计数、事务语义、迭代器失效条件
2警惕容器无关代码的幻觉:不同容器接口差异很大,强行通用化会丧失性能和表达能力
3让拷贝廉价而正确:容器中的对象必须能正确拷贝,否则行为未定义
4用empty()代替检查size()==0:对所有容器都高效;某些容器的size()是O(n)
5优先使用范围成员函数:insert(pos, first, last) 比逐个insert更高效
6警惕C++最烦人的解析:Foo x(); 被解析为函数声明,非对象定义
7容器存new的指针时记得delete:容器销毁时不会自动delete指针指向的对象
8绝不在容器中放auto_ptr:auto_ptr的拷贝语义与容器要求冲突
9谨慎选择删除方式:erase返回新迭代器(sequence容器)或void(associative容器)
10了解allocator约定与限制:不同容器的allocator可能不等价
11理解自定义allocator的合法用途:内存池、调试追踪等
12对STL容器线程安全保持现实期望:只有”同一容器上的不同读操作”才线程安全

vector与string(Items 13-18)

Item要点
13优先用vector/string代替动态数组:自动内存管理、异常安全
14用reserve()避免不必要的重新分配:预估容量,提前reserve
15了解string实现的变体:SSO(短字符串优化)、引用计数等
16知道如何把vector/string数据传给遗留API:&v[0] 或 v.data()
17用”swap技巧”修剪多余容量:vector<T>(v).swap(v)
18避免使用vector:它是个特化,行为不像普通vector

关联容器(Items 19-25)

Item要点
19理解相等与等价的区别:== vs operator<(等价)
20为指针的关联容器指定比较类型:默认按地址比较,通常不是你想要的
21比较函数对相等值必须返回false:违反此规则导致未定义行为
22避免在set/multiset中就地修改key:会破坏容器排序,导致未定义行为
23考虑用排序vector替代关联容器:内存 locality 更好,遍历更快
24效率敏感时用insert而非operator[]:operator[] 在无元素时会插入默认值
25熟悉非标准哈希容器:hash_set/hash_map等(SGI扩展,非C++标准)

迭代器(Items 26-29)

Item要点
26优先用iterator而非const_iterator:需要时可以隐式转换
27用distance和advance转换const_iterator:const_iterator不能直接加减
28理解reverse_iterator的base():base()指向”当前元素的下一个”
29考虑用istreambuf_iterator做逐字符输入:比istream_iterator更高效

算法(Items 30-37)

Item要点
30确保目标范围足够大:用back_inserter等插入迭代器避免越界
31了解你的排序选项:sort/stable_sort/partial_sort/nth_element各有适用场景
32remove类算法后要接erase:remove不真正删除元素,只”移走”
33对指针容器慎用remove类算法:remove的是指针值,不是所指对象
34注意哪些算法要求有序范围:binary_search/upper_bound/lower_bound/equal_range
35用mismatch或lexicographical_compare做大小写不敏感比较
36理解copy_if的正确实现:C++98中没有copy_if,需自己实现
37用accumulate或for_each做范围汇总

Functors与函数对象(Items 38-42)

Item要点
38设计functor类时按值传递:避免引用悬空
39使谓词为纯函数:不修改状态,保证可重入性
40使functor类可适配:提供typedef使算法能正确使用
41理解ptr_fun/mem_fun/mem_fun_ref的原因:将函数指针/成员函数转为functor
42确保less意味着operator<:否则关联容器行为未定义

STL编程(Items 43-50)

Item要点
43优先用算法调用代替手写循环:更简洁、更高效、更少bug
44优先用成员函数代替同名算法:list::sort() 比 std::sort() 更高效
45区分count/find/binary_search/lower_bound/upper_bound/equal_range
46考虑用函数对象代替函数作为算法参数:可携带状态
47避免写出难以阅读的代码:过度泛化是维护噩梦
48始终包含正确的头文件:<algorithm> 不等于 <vector>
49学会解读STL相关的编译器诊断:用typedef展开模板类型
50熟悉STL相关网站:SGI STL、STLport、Boost

核心思维模型

连续内存容器 vs 节点容器

特性连续内存(vector/deque/string)节点(list/set/map)
内存布局连续块分散节点
插入/删除中间移动元素,O(n)改指针,O(1)
迭代器失效可能全部失效仅指向被删元素的失效
缓存友好好差
随机访问O(1)O(n)

remove-erase惯用法

// 错误:remove不真正删除
std::remove(v.begin(), v.end(), value);
 
// 正确:remove + erase
v.erase(std::remove(v.begin(), v.end(), value), v.end());

容器选择决策树

需要随机访问? ──是──→ vector
     │
     否
     ↓
需要在中间频繁插入/删除? ──是──→ list
     │
     否
     ↓
需要在首尾频繁插入/删除? ──是──→ deque
     │
     否
     ↓
需要排序/去重/快速查找? ──是──→ set/map
     │
     否
     ↓
需要哈希查找? ──是──→ unordered_set/unordered_map(C++11)

与《Essential C++》的关系

  • Essential C++:系统性入门,理解C++语言特性
  • Effective STL:进阶最佳实践,理解STL的正确使用方式
  • 建议先读Essential C++第3章(泛型编程),再精读Effective STL

可行动点

  1. 立即实践:把所有手写循环替换为算法调用(Item 43)
  2. 代码审查重点:检查是否有vector<bool>、auto_ptr、未接erase的remove(Item 7/8/32)
  3. 性能优化:对vector提前reserve(Item 14),对list用成员sort(Item 44)
  4. 编译器诊断:学会用typedef展开模板类型,快速定位bug(Item 49)