Effective STL 读书笔记 6

来源:互联网 发布:java正则表达式匹配url 编辑:程序博客网 时间:2024/05/17 02:33
Item 30:在应用算法时,确定目标范围(destination ranges)足够大。
  • 首先以下代码是错误的,transform 将对 dest.end() 开始的 N 个元素调用赋值操作,但这些元素并不存在:
transform(src.begin(), src.end(),
          dest.end(),
          transmogrify);
  • 正确的方法是先创建对象(push_back),再赋值,如下:
transform(src.begin(), src.end(),
          back_inserter(dest), // or front_inserter(dest),
          transmogrify);
  • 如果想在任意位置插入对象:
transform(src.begin(), src.end(),
          inserter(dest, dest.begin() + dest.size() / 2), // at middle
          transmogrify);
  • 上面代码在处理 string 和 vector 容器时,效率较低,可以遵照 Item 14 的方法使用 reserve:
dest.reserve(dest.size() + src.size());
transform(src.begin(), src.end(),

          back_inserter(dest),
          transmogrify);
  • 注意即使你已经使用了 reserve 创造了足够的空间,但并不能省去 inserter 的调用,因为 reserve 创造的仅仅是空间,而没有构造对象,而 transform 函数是针对已存在的对象调用赋值操作。
  • 如果并不许要插入对象而仅仅是改变(overwrite)容器内的值,可以使用:
dest.resize(src.size());
transform(src.begin(), src.end(),
          dest.begin(),
          transmogrify);
  • 或者:
dest.clear();
dest.reserve(src.size());

transform(src.begin(), src.end(),
          back_inserter(dest);
          transmogrify);
  • 在调用算法时,必须确定该算法所需要的空间足够,空间是否会随着算法自动增加。增加空间的方法是调用 insert iterators,包括 ostream_iterator 及上面的 back_inserter, front_inserter, inserter。

Item 31:了解 sort 的选项,你并不总是需要全排序。
  • 我们要排序,但并不总是需要全排序,有时我们仅仅需要 Top 10,有时我们只关心 Sub Contents,对这些需求作全排序太浪费计算资源,本条目介绍了几种 sub sort 算法。
  • partial_sort 可以看作是 Top N 算法,将最小容器中最小的 N 个元素按升序排列在容器头部。
  • nth_element 和上面的算法接近,将最小的 N 个元素放在容器头部,但并没有对它们排序。
  • 上面两个排序算法都是非 stable 的,一个 stable 的排序算法不会改变两个相等(等价)对象的顺序(排序之前的顺序)。STL 中提供了一个 stable 的排序算法:stable_sort
  • nth_element 还可以用作查找目标容器内排序后的某个特殊值,就像其名字“第 n 个元素”:
vector<int> data;
...
vector<int>::iterator goal = data.begin() + 5;
nth_element(data.begin(), goal, data.end(), my_data_compare);
  • 上面代码执行完后 goal 指向的对象是容器中第 5 小的元素。
  • partition 用来将容器内的元素按照指定的规则分成两部分,函数的返回值为分界线。与上面算法不同的是不需要指定个数 N,仅仅指定划分规则。
  • stable_partition 与上相同,但并不改变相同元素的原有顺序。
  • 按照性能排序:
  1. partition
  2. stable_partition
  3. nth_element
  4. partial_sort
  5. sort
  6. stable_sort

Item 32:在 remove 后紧跟 erase 确保元素被删除。
  • 首先 remove 不能够删除任何元素,因为其参数中没有容器本身,只有迭代器。除了 list::remove,因为是成员函数所以有能力删除元素。
  • remove 算法仅仅是用 unremoved 的元素来覆盖 removed 的元素,然后返回 middle。从 begin 到 middle 间的元素是未被删除的,而 middle 到 end 间的元素并没有任何改变。(但也已经没有任何用处)。即 [1, 2, 3, 99, 5, 99, 7, 8, 9, 99] 被 remove(99) 后变成 [1, 2, 3, 5, 7, 8, 9, 8, 9, 99],红色部分为未改变部分。
  • 应该用如下方法删除元素:
vector<int> v;
...
v.erase(remove(v.begin(), v.end(), 99), v.end());
  • remove 类的算法还有:remove_ifunique

Item 33:当心在指针容器上调用的 remove 算法。
  • remove 覆盖而不删除,如果容器存储的是指针,为防止资源泄露,应尽量避免使用 remove 类的算法。考虑用 partition 代替。
  • 如必须要使用:
void deleteAndNullifyUncertified(Object *& obj) {
    if (!obj->isCertified()) {
       delete obj;
       obj = 0;
    }
}
for_each(v.begin(), v.end(), deleteAndNullifyUncertified);
v.erase(remove(v.begin(), v.end(),
               static_cast<Object *>(0)),
        v.end());
  • 也可以用 smart_point 来代替原始指针存入容器。(smart_point 可以参考 boost)

Item 34:注意,有些算法需要 sorted ranges。
  • 以下算法需要 sorted ranges:
binary_search
lower_bound
upper_bound
equal_range

set_union
set_intersection
set_difference
set_symmetric_difference

merge // get two sorted range, return a merged sorted range.
inplace_merge
includes // whether a range is include another.
  • 以下算法一般用在 sorted ranges 上,但并不是必须。
unique
unique_copy
  • unique 将同一相等组内的对象只保留一个。例如:[1,2,1,3,3,3,1] --> [1,2,1,3,1,3,1],红色为需要 erase 的部分,其中 1 虽然出现了多次但并不在一个“相等组”内。
  • 注意,在调用这些算法的时候所使用的比较函数(对象)必须与排序算法使用的相同!