stl sort分析

转载:http://www.cnblogs.com/yuanzz/p/3735213.html

最近写代码,无意中发现了一个坑,关于自定义比较函数的stl sort函数的坑,于是记录下来。

先贴代码:

 1 #include <iostream>2 #include <vector>3 #include <algorithm>4 5 struct finder6 {7         bool operator()(int first, int second){return first <= second;}8 } my_finder;9
10 int main(int argc, char** argv)
11 {
12         int value = atoi(argv[1]);
13         int num = atoi(argv[2]);
14         std::vector<int> vecTest;
15         for(int i=0; i!=num; ++i)
16                 vecTest.push_back(value);
17
18         std::sort(vecTest.begin(), vecTest.end(), my_finder);
19         for(int i=0; i!=vecTest.size(); ++i)
20                 std::cout<<vecTest[i]<<'\t';
21         std::cout<<std::endl;
22
23         return 0;
24 }

这段代码看上去好好的,实际上却有core的可能。

且看图:

敏思苦想很久,也想不出为啥会core,后来查了资料,才发现了问题所在,现在通过源码分析一下原因。

于是定位到sort函数:

 1   template<typename _RandomAccessIterator, typename _Compare>2     inline void3     sort(_RandomAccessIterator __first, _RandomAccessIterator __last,4          _Compare __comp)5     {6       typedef typename iterator_traits<_RandomAccessIterator>::value_type7         _ValueType;8 9       // concept requirements
10       __glibcxx_function_requires(_Mutable_RandomAccessIteratorConcept<
11             _RandomAccessIterator>)
12       __glibcxx_function_requires(_BinaryPredicateConcept<_Compare, _ValueType,
13                                   _ValueType>)
14       __glibcxx_requires_valid_range(__first, __last);
15
16       if (__first != __last)
17         {
18           std::__introsort_loop(__first, __last, __lg(__last - __first) * 2,
19                                 __comp);
20           std::__final_insertion_sort(__first, __last, __comp);
21         }
22     }

这是stl_algo.h中的sort函数,且忽略10-14行的参数检查,实际上sort函数先是用了introsort(内省排序,http://en.wikipedia.org/wiki/Introsort),然后采用了insertsort(插入排序)。

1、我们先来分析内省排序吧。

  先来看看__introsort_loop的函数原型

 1   template<typename _RandomAccessIterator, typename _Size>2     void3     __introsort_loop(_RandomAccessIterator __first,4                      _RandomAccessIterator __last,5                      _Size __depth_limit)6     {7       typedef typename iterator_traits<_RandomAccessIterator>::value_type8         _ValueType;9
10       while (__last - __first > int(_S_threshold))
11         {
12           if (__depth_limit == 0)
13             {
14               std::partial_sort(__first, __last, __last);
15               return;
16             }
17           --__depth_limit;
18           _RandomAccessIterator __cut =
19             std::__unguarded_partition(__first, __last,
20                                        _ValueType(std::__median(*__first,
21                                                                 *(__first
22                                                                   + (__last
23                                                                      - __first)
24                                                                   / 2),
25                                                                 *(__last
26                                                                   - 1))));
27           std::__introsort_loop(__cut, __last, __depth_limit);
28           __last = __cut;
29         }
30     }

  如果__last - __first > int(_S_threshold)的时候,就开始循环了。

  关于_S_threshold的定义:

1 enum { _S_threshold = 16 };

  好吧,是写死的,为【16】

  注意,我为什么把16标红,这就是坑开始的地方了。如果元素小于16(第10行),就直接略过,开始了:

  std::__final_insertion_sort(__first, __last, __comp);  // 本次不对其进行分析

  我们继续往下走,__depth_limit哪来的呢。看代码:

  template<typename _Size>inline _Size__lg(_Size __n){_Size __k;for (__k = 0; __n != 1; __n >>= 1)++__k;return __k;}

  还记得sort调用introsort吗?

  std::__introsort_loop(__first, __last, __lg(__last - __first) * 2, __comp);

  当__depth_limit != 0时,则开始了introsort递归,而真正影响它的是__unguarded_partition函数。

  __unguarded_partition函数原型:

 1   template<typename _RandomAccessIterator, typename _Tp, typename _Compare>2     _RandomAccessIterator3     __unguarded_partition(_RandomAccessIterator __first,4                           _RandomAccessIterator __last,5                           _Tp __pivot, _Compare __comp)6     {7       while (true)8         {9           while (__comp(*__first, __pivot))
10             ++__first;
11           --__last;
12           while (__comp(__pivot, *__last))
13             --__last;
14           if (!(__first < __last))
15             return __first;
16           std::iter_swap(__first, __last);
17           ++__first;
18         }
19     }

  好吧,终于要找到原因了,就是这个__pivot了。

  还记得我们自定义的__comp函数吗?  

  struct finder
  {
    bool operator()(int first, int second){return first <= second;}
  } my_finder;

  当*__first == __pivot的时候,返回了true,然后,执行了16行的元素交换。

  那如果像我测试的代码那样,所有元素都相等呢?岂不就是走进了死循环,等着的就可能是越界了。

  为什么__unguarded_partition不检查边界呢?有人分析称是为了效率,再大数据排序的时候,每次都需要校验边界,确实也是个很大开销。

  后来看人总结:永远让比较函数对相等的值返回false(来自Effective C++)

  附上正确代码: 

 1 #include <iostream>2 #include <vector>3 #include <algorithm>4 5 struct finder6 {7         //永远让比较函数中对相等值返回false8         bool operator()(int first, int second){return first < second;}9 } my_finder;
10
11 int main(int argc, char** argv)
12 {
13         int value = atoi(argv[1]);
14         int num = atoi(argv[2]);
15         std::vector<int> vecTest;
16         for(int i=0; i!=num; ++i)
17                 vecTest.push_back(value);
18
19         std::sort(vecTest.begin(), vecTest.end(), my_finder);
20         for(int i=0; i!=vecTest.size(); ++i)
21                 std::cout<<vecTest[i]<<'\t';
22         std::cout<<std::endl;
23
24         return 0;
25 }

结果:

转载于:https://www.cnblogs.com/fartherfuture/p/3735298.html

stl sort分析相关推荐

  1. 不可不知的STL sort函数实现原理

    sort函数一直以来被认为是快排,今天看到一篇文章,感觉自己知道的太少. 建议大家还是要去啃<STL源码剖析>,我也要去读了,先立个flag,后续1-2个月写STL源码剖析上得到的启发. ...

  2. STL sort()函数详解

    西方有句谚语:不要重复发明轮子! STL几乎封装了所有的数据结构中的算法,从链表到队列,从向量到堆栈,对hash到二叉树,从搜索到排序,从增加到删除......可以说,如果你理解了STL,你会发现你已 ...

  3. STL源代码分析(ch 1)概述

    1. 6大组件 容器(containers):各种数据结构,如vector.list.deque.set.map,用来存放数据.从实现来看,STL容器是一种 class template. 算法(al ...

  4. STL sort解析

    从上学接触到编程开始,到工作了几年.有关排序算法的内容反反复复有接触,但是要说每一种排序算法的细节都能说清,那就有难度了. 一来算法难度有深有浅.有比较简单的冒泡,插入,也有复杂的堆排序,快排这些. ...

  5. c语言sort函数排序二维数组,c++ - 如何使用stl sort函数根据第二列对二维数组进行排序? - 堆栈内存溢出...

    stl排序要求迭代器的rvalue作为参数传递. 如果你想使用sort函数,你必须在c ++ 11中编译并使用数组stl来存储数组. 代码如下 #include "bits/stdc++.h ...

  6. 18118 勇者斗恶龙(c++ STL sort的运用)

    18118 勇者斗恶龙 Description 有n个头的恶龙,你希望雇一些骑士把它杀死(即砍掉所有头).村里有m个骑士可以雇佣,一个能力值为x的骑士可以砍掉恶龙 一个直径不超过x的头,且需要支付x个 ...

  7. 火眼睛睛查coredump(stl sort)------永远让比较函数对相同元素返回false

    转载:http://blog.csdn.net/stpeace/article/details/51040218#cpp 看看如下代码的一个非常隐晦的错误, 虽然不会每次core dump, 但类似代 ...

  8. STL 源代码分析 算法 stl_algo.h -- includes

    本文senlie原,转载请保留此地址:http://blog.csdn.net/zhengsenlie includes(应用于有序区间) ------------------------------ ...

  9. STL源代码分析(ch2 内存分配)uninitialized_fill_n

    1. uninitialized_fill_n(ForwardIter first, Size n, const T& value) 从 first 位置开始,填充 n 个元素值,返回填充结束 ...

  10. STL源代码分析(ch2 内存分配)destroy

    1. 两个版本 1.1 参数是指针 template <class T> inline void destroy(T* pointer) {pointer->~T(); // 调用析 ...

最新文章

  1. springboot取yml中的值_SpringBoot 中从yml配置文件中读取常用的参数值
  2. 基于asp.net2.0中membership系统角色、用户、资源的权限设计
  3. Windows8 Metro开发 (04) : 保存/读取本地应用程序设置
  4. 02-JDBC学习手册:JDBC编程步骤【重点重点】
  5. Boost:不受约束的bimap双图的测试程序
  6. raspberry pi_如何使用Raspberry Pi设置个人Web服务器
  7. linux实现普通用户只允许使用部分命令
  8. 服务器远程预览本地设备视频, 预览多个,打不开视频流
  9. ORACLE认证考试之ocjp 考试题之五
  10. 8051蜂鸣器程序c语言,单片机驱动蜂鸣器C51程序
  11. 读书笔记:《过程咨询 II》
  12. 太阳能最佳倾角c语言编程,用C语言编程计算太阳的高度角,用于控制太阳能电板最佳的倾角...
  13. PWM频率与占空比的关系
  14. 超级计算机不必采用并行技术,高速并行光互联技术及其应用-通信/网络-与非网...
  15. 45、优惠券秒杀(实现一人一单)
  16. front-matter参数的解释与设置
  17. 51假期读书笔记(上)——流畅的python
  18. Windows7下安装Docker、下载镜像和运行OpenTsdb容器
  19. Python:类和文件操作
  20. Android Google Maps教程

热门文章

  1. C++ 面向对象编程
  2. mysq;多表查询 总结
  3. php socket 超时设置
  4. 多linux服务器之间实现文件自动复制(脚本)
  5. Configure universal group membership caching
  6. php zip类,php ZIP压缩类实例步骤详解
  7. hinernate中持久化对象的状态
  8. mybatis注册映射文件
  9. linux 键盘 键值0x1e,Linux文本处理三剑客之awk学习笔记11:选项、内置变量和内置函数...
  10. while循环python的范围_为什么Python中的range()循环比使用while循环更快?