Effective STL: Item 44:优先使用与泛型算法同名的成员函数

原创 2003年03月11日 09:35:00

 <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

Item 44:优先使用与泛型算法同名的成员函数

一些容器拥有和STL泛型算法同名的成员函数。关联容器提供count()、find()、lower_bound()、upper_bound(),和equal_range(),而list提供了remove()、remove_if()、unique()、sort()、merge(),和reverse()。这样做有两个理由。首先,成员函数更快。其次,它们与容器结合得更好(尤其是关联容器)。 那是因为同名的泛型算法和成员函数通常并不是以完全相同的方式来做同样的事。

我们以对关联容器的试验开始。假如有一个set<int>,它存储了一百万个元素,而你想找到元素727的第一个发生位置(如果它存在的话)。有两个最然的方法来执行搜索:

set<int> s; // create set, put

... // 1,000,000 values

// into it

set<int>::iterator i = s.find(727); // use find member

if (i != s.end()) ... // function

set<int>::iterator i = find(s.begin(), s.end(), 727); // use find algorithm

if (i != s.end()) ...

成员函数的find()运行对数时间,所以不管727是否存在于set中,set::find()只需执行不超过40次比较来查找它,而平均只需要20次。相比而言,泛型算法的find()运行线性时间,所以如果727不在set中时它需要执行1,000,000次比较。即使727在set中,也平均需要执行500,000次比较来定位它。效率得分如下:

成员的find():大约40(最坏情况)到大约20(平均情况)

泛型算法find():1,000,000(最坏情况)到500,000(平均情况)

和高尔夫比赛一样,分值低的赢。如你所见,比赛结果毫无争议可言。

我必须对成员函数的find()所需的比较次数表示小小的谨慎,因为它有些依赖于关联容器的实现。绝大部分的实现是使用的红黑树,平衡树的一种,失衡度可能达到2(that may be out of balance by up to a factor of two)。在这样的实现中,对一百万个元素的set进行搜索,所需的最大比较次数是38次。但对绝大部分的搜索情况而言,不超过22次。一个基于完全平衡树的实现绝不需要超过21次比较,但在实践中,完全平衡树的效率总得来说不如红黑树。这就是为什么绝大多数的STL实现都使用红黑树。

效率不是成员函数的find()和泛型算法find()间的唯一差别。如Item 19所解释,STL泛型算法判断两个对象是否相同,检查的是equality(WQ注:逻辑相等,使用operator==),而关联容器检测的是equivalence(WQ注:数学相等,two elements are equal if neither is less than the other)。 因此,泛型算法find()搜索727用的是逻辑相等,而成员函数的find()用的是数学相等。逻辑相等和数学相等间的区别可以造成成功搜索和不成功搜索的区别。举例来说,Item 19演示了用泛型算法find()在关联容器搜索失败而用成员函数的find()搜索却成功的情况!因此,如果操纵关联容器的话,你应该优先使用成员函数形式的find()、count()、lower_bound()等函数,而不是同名的泛型算法,因为这些成员函数版本提供了和其它成员函数相一致的行为。由于逻辑相等和数学相等间的差别,泛型算法不能提供这样的一致行为。

这一差别对map和multimap尤其明显,因为它们容纳的是pair型对象,而它们的成员函数只在意pair对象的key部分。因此,成员函数count()只统计key值匹配的pair对象的数目 (“匹配”,自然是检测含义相等);pair的value部份被忽略。成员函数的find()、lower_bound()、upper_bound(),以及equal_range()同样如此。如果你使用泛型算法find(),于是,它的寻找将基于(a)逻辑相等和(b)pair的全部组成部分;泛型算法的find()、lower_bound()等同样如此。要想让泛型算法只关注于pair的key部分,必须要跳出Item 23描述了的限制(那儿介绍了用数学相等代替逻辑等价作测试的方法)。

另一方面,如果你真的关心效率,你可以决定采用Item 23中的技巧,协同Item34中讲的对数时间搜索算法。相对于性能的提升,这只是一个很小的代价。再者,如果你真的在乎效率,你应该考虑非标的hash容器(在Item 25进行了描述),只是,你将再次面对逻辑相等和数学相等的区别。

对于标准的关联容器,选择成员函数而不是泛型算法,有几个好处。首先,你得到的是对数时间而不是线性时间的性能。其次,你判断两个元素“相同”使用的是数学相等,这是关联容器的默认定义。第三,当操纵map和multimap时,你自动地只处理key值而不是(key, value)对。这三点给了优先使用成员函数充分的理由。

让我们转到list的与泛型算法同名的成员函数身上。这里的故事几乎全部是关于效率的。每个被list作了特别化的泛型算法(remove()、remove_if()、unique()、sort()、merge(),和reverse())都要拷贝对象,而list的特别版本不拷贝任何东西;它们只是简单地操纵连接list的节点的指针。泛型算法和成员函数的算法复杂度是相同的,但如果操纵指针比拷贝对象的代价小的话,list的版本应该提供更好的性能。

牢牢记住这一点很重要:list的成员函数版本的行为和同名的泛型算法版本,行为经常不相同。如Item 32所解释,调用泛型算法的remove()、remove_if(),和unique(),必须紧跟着调用erase()函数(如果你真的想从容器中清除对象的话);但list的remove()、remove_if()和unique()成员函数真的去掉了元素,不需要接着调用erase()。

在泛型算法sort()和list的sort()成员函数间的一个重要区别是:前者不能用于list。作为单纯的双向iterator,list的iterator不能传给泛型算法sort()。Merge()泛型算法和list的merge()成员函数之间也同样存在巨大差异。这个泛型算法不被允许修改源范围,但list::merge()同样可以修改它的宿主list。

所以,你明白了吧。 当面临在同名的STL泛型算法和容器成员函数间进行选择时,你应该优先使用成员函数。它几乎肯定更高效,而且它看起来和容器的惯常行为集成得更好。

 

 

《Effective C++》:条款44-条款45

条款44将与参数无关的代码抽离templates 条款45运用成员函数模板接受所有兼容类型...
  • KangRoger
  • KangRoger
  • 2015年03月12日 22:01
  • 1482

C++ STL优先队列常用用法

STL优先队列
  • CerberuX
  • CerberuX
  • 2016年06月26日 13:08
  • 964

effective stl 第19条:理解相等(equality)和等价(equivalence)的区别

#include #include #includeusing namespace std;bool ciStringCompare(const string l, const string r) {...
  • u014110320
  • u014110320
  • 2016年09月20日 23:36
  • 238

《Effective C++》设计与声明:条款18-条款19

这两个条款讲的是:接口的设计和类的设计。其中接口的设计原则是让接口容易被正确使用,不容易被误用;后面有一系列的做法。类的设计,讲的是类设计犹如新类型type的设计。在设计类时要考虑的一系列问题。...
  • KangRoger
  • KangRoger
  • 2015年01月21日 21:43
  • 1323

STL优先队列详解

优先队列           优先队列是一种抽象数据类型(Abstract Date Type,ADT),行为和队列类似,但是先出队的元素不是先进队列的元素,而是队列中优先级最高的元素。 ...
  • chaiwenjun000
  • chaiwenjun000
  • 2015年05月03日 10:11
  • 938

STL中堆和优先队列的使用方法

一、heap #include make_heap(首位置, 尾位置+1, 可选的cmp函数);  -> 构造堆,将数组堆化 push_heap(首位置, 尾位置+1, 可选的cmp函数);  ->...
  • DjangoBUAA
  • DjangoBUAA
  • 2016年04月20日 14:31
  • 1476

【原+转】STL之优先队列的cmp函数和sort以及qsort函数的cmp

STL之优先队列的cmp函数和sort以及qsort函数的cmp 首先,我们来谈谈大名鼎鼎的void qsort(void *base,int nelem,int width,int (*fcmp)(...
  • ly59782
  • ly59782
  • 2016年11月14日 22:39
  • 1157

Item 31:最小化文件之间的编译依赖 Effective C++笔记

Item 31: Minimize compilation dependencies between files. 曾听老师讲过,每天上班的第一件事就是下载最新代码开始编译,然后可以有半个小...
  • yangjvn
  • yangjvn
  • 2015年09月19日 11:56
  • 843

【STL学习】优先级队列Priority Queue详解与C++编程实现

优先级队列Priority Queue介绍 优先级队列是一个拥有权值观念的queue。它允许在底端添加元素、在顶端去除元素、删除元素。 优先级队列内部的元素并不是按照添加的顺序排列,而是自...
  • xiajun07061225
  • xiajun07061225
  • 2013年01月30日 16:43
  • 14027

Sort排序+STL队列+优先队列小结

Sort排序+STL队列+优先队列小结
  • u011074149
  • u011074149
  • 2013年07月26日 13:40
  • 1866
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Effective STL: Item 44:优先使用与泛型算法同名的成员函数
举报原因:
原因补充:

(最多只允许输入30个字)