0%

Effective_STL 阅读笔记(四)

引言

STL中为了更好的遍历、访问数据,为容器提供了迭代器。不同于for循环的遍历方式,迭代器不会暴露数据内部的联系,保证数据的安全。有了迭代器的基石,STL又为容器提供了诸多的算法,大大提高了数据的处理效率。处理数据时,迭代器的与算法的选择则会成为我们优化代码的一个基本门槛。

迭代器与算法

STL内部一共提供了四种迭代器: iterator、const_iterator、reverse_iterator、const_reverse_iterator。

迭代器的区别

默认情况下,四种迭代器都是可以选择的,它们的直观的区别在于其功能与作用:

  1. iterator 作用是常规迭代器,功能相当于T*
  2. const_iterator 作用是常量类型迭代器,功能相当于const T*
  3. reverse_iterator 作用是反向迭代器,功能相当于T*
  4. const_reverse_iterator 作用是反向常量迭代器,功能相当于const T*

它们之间有着明确的转化关系,iterator可以隐式转化为const_iterator或reverse_iterator迭代器。const_iterator与reverse_iteraotr,可以转化为const_reverse_iterator迭代器。reverse_iterator可通过base()转化为iterator,const_reverse_iterator可以通过base()转化为const_iterator。

这里通过base()进行的迭代类型的转换,最后的结果可能并非是我们想要得到的内容。由于base()与隐式转换可能导致的一些潜在的问题,为了避免最好使用默认的iterator迭代器。

const_iterator转化iterator

  1. 创建一个iterator迭代器
  2. 将iterator指针指向 const_iterator初始位置

算法中的区间目标

当新对象添加进来时,总是认为STL会为数据准备好了空间,可以直接使用而不关心。但事实上并非如此,如下例所示:

1
2
3
4
5
6
int transmogrify(int x) // 生成新值
vector<int> ans;

// 使用transform向目标区别的尾部添加新值
vector<int> res;
transform(ans.begin(), ans.end(), res.end(), transmogrify);

这样有一个问题,即res.end()本身是没有对象,那么开辟*(res.end())+1同样也是没有对象的。显而易见,这会向一个不存在的地址写入数据,而这样会导致程序出错。

STL提供了back_inserter来处理,其实就是调用back_push来插入数据,这样在扩展数据容量的同时,也保证了数据写入的安全性。当然,如果有头部插入的需求,我们可以使用front_inserter来处理。
当然,你考虑到vector扩容所带来的影响,你也可以使用reserve来对vector的capacity进行预扩展,记住这是capacity,不是真正的size,若是使用transfrom安全的方式插入仍会导致程序错误。

算法之排序

若是提到排序,STL中的sort必然是首推,但是并非所有情况下的它都是最优的选择。如下面各种不同的场景。

  1. 有堆刚刚到货物,仅需要排序出前十个最好货物时,可以使用parital_party来进行处理(部分排序)。
  2. 货物到货之后,销售人员告知不需要排序,仅仅需要将找到的十个最好的给她即可,即可使用nlt_element(区间排序)

这两个排序算法: parital_party与nlt_element都是非稳定的排序算法。非稳定性即,在遇到两个等价的值时,排序算法会将两个数据进行交换。而需要稳定性算法时,我们可以使用STL提供的stale_sort来处理。

若有对将特定元素放到元素前部的要求时,我们可以使用parition算法。当然对于不稳定的它,STL也提供了stable_parition。
要是没有稳定性的考虑的话,效率优先最好不选用stable算法。

remove的实质

remove是无法做到删除的,它操作的结果是覆盖。若是真正的需要删除,就请使用erase_remove手法进行删除。
若是遇到一些对象是指针的容器,我们需要小心处理,因为erase会删除容器中的指针,但并不会删除指针所指向的对象,这就会导致资源的泄漏。
为也避免,我们需要在使用erase(remove)手法删除前,对数据进行验证并将其置空,之后通过erase(remove)手法清除空指针。
这样处理资源的方式,终就会存在一定的风险,若是想要安全保险,最好还是使用智能指针来管理你的容器对象。

结语

迭代器根据作用有不同的适用场景,而安全又保险的使用则是默认的iterator迭代器。算法有很多,但是不要认为STL会为我们提供好一切,它仅提供操作,内部的资源管理我们仍需要小心。