Hello_Motty

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

 

  这几天在准备换工作,所以总是没机会来写博客,我同事总说他想看书,但是那么厚厚的一本总让人望而却步,我觉得几天看一本书并非不可能,重点是知道自己要了解什么,就像查字典一样,而不是看电视剧一样。

条款31:了解你的排序选择

  如果你需要在vector、string、deque或数组上进行完全排序,你可以使用sort或stable_sort;如果你有一个vector、string、deque或数组,你只需要排序前n个元素,应该用partial_sort;如果你有一个vector、string、deque或数组,你需要鉴别出第n个元素或你需要鉴别出最前的n个元素,而不用知道它们的顺序,nth_element是你应该注意和调用的;如果你需要把标准序列容器的元素或数组分隔为满足和不满足某个标准,你大概就要找partition或stable_partition;如果你的数据是在list中,你可以直接使用partition和stable_partition,你可以使用list的sort来代替sort和stable_sort。性能方面:partition > stable_partition > nth_element > partial_sort > sort > stable_sort。

条款32:如果你真的想删除东西的话就在类似remove的算法后接上erase

  remove接收指定它操作的元素区间的一对迭代器,,所以remove不知道它作用于哪个容器。此外,remove也不可能发现容器,因为没有办法从一个迭代器获取对应于它的容器。从容器中除去一个元素,唯一的方法是调用那个容器的一个成员函数,几乎都是erase的某个形式。从一个容器中remove元素不会改变容器中元素的个数。你可以想象remove完成了一种压缩,被删除的值表演了在压缩中被填充的洞的角色,而其他值则是完成填洞行为。remove_if和unique与remove类似。

条款33:提防在指针的容器上使用类似remove的算法

  按照条款32中所描述的,会让不符合的元素被覆盖,如果元素为指针,则在指针释放前就被被覆盖了,导致内存泄漏。

  remove执行前                                                                                            remove执行后

 

  所以在删除前,应该找到所有不符合要求的指针,释放资源并置空,然后再对空指针进行覆盖和删除。

条款34:注意哪个算法需要有序区间

  binary_search  lower_bound  upper_bound   equal_range  set_union   set_intersection  set_difference   set_symmetric_difference  merge   inplace_merge  includes需要有序区间。

   unique   unique_copy一般用于有序区间,但是它们没这么要求。

  比较函数和区间的顺序应该一致。

条款35:通过mismatch或lexicographical比较实现简单的忽略大小写字符串比较

  首先先写一个比较函数:

 

 1 int ciCharCompare(char c1, char c2) // 忽略大小写比较字符
 2 { // c1和c2,如果c1 < c2返回-1,如果c1==c2返回0,如果c1 > c2返回1
 3     int Ic1 = tolower(static_cast<unsigned char>(c1));// 这些语句的解释
 4     int Ic2 = tolower(static_cast<unsigned char>(c2));// 看下文
 5     if (Ic1 < Ic2) return -1;
 6     if (lc1 > Ic2) return 1;
 7     return 0;
 8 }
 9 
10 int ciStringCompare(const string& s1, const string& s2)
11 {
12 if (s1.size() <= s2.size()) return ciStringCompareImpl(s1, s2);
13 else return -ciStringCompareImpl(s2, s1);
14 }
15 int ciStringCompareImpl(const string& si, const strings s2)
16 {
17     typedef pair<string::const_iterator, // PSCI = “pair of
18     string::const_iterator> PSCI; // string::const_iterator”
19     PSCI p = mismatch( s1.begin(), s1.end()
20         s2.begin(), 
21         not2(ptr_fun(ciCharCompare)));
22     if (p.first== s1.end()) { 
23         if (p.second == s2.end()) return 0;
24         else return -1;
25     }
26     return ciCharCompare(*p.first, *p.second); // 两个字符串的关系
27 } // 和不匹配的字符一样

 

  mismatch的判断式,也就是not2(ptr_fun(ciCharCompare)),当字符匹配时这个判断式返回true,因为当判断式返回false时mismatch会停止。我们不能为此使用ciCharCompare,因为它返回-1、1或0,而当字符匹配时它返回0,就像strcmp。如果我们把ciCharCompare作为判断式传给mismatch,C++会把ciCharCompare的返回类型转换为bool,而当然bool中零的等价物是false,正好和我们想要的相反!同样的,当ciCharCompare返回1或-1,那会被解释成true,因为,就像C,所有非零整数值都看作true。

  另一种是使用ciStringCompare做STL判断式,然后把比较工作交给strcmp的泛型版本lexicographical_compare。就像strcmp,lexicographical_compare认为两个相等值的区间是相等的,因此它对于这样的两个区间返回false:第一个区间不在第二个之前。也像strcmp,如果第一个区间在发现不同的对应值之前就结束了,lexicographical_compare返回true:一个先于任何区间的前缀是一个前缀。

条款36:了解copy_if的正确实现

 1 template<typename InputIterator, // 一个copy_if的
 2     typename OutputIterator, // 正确实现
 3     typename Predicate>
 4 OutputIterator copy_if(InputIterator begin,
 5     InputIterator end,
 6     OutputIterator destBegin,
 7     Predicate p) {
 8     while (begin != end) {
 9         if (p(*begin))*destBegin++ = *begin;
10         ++begin;
11     }
12     return destBegin;
13 }

  放在你局部的STL相关工具库中,而且只要合适就使用。

条款37:用accumulate或for_each来统计区间

  accumulate,不像大部分算法,它不存在于<algorithm>。取而代之的是,它和其他三个“数值算法”都在<numeric>中。(那三个其它的算法是inner_product、adjacent_difference和partial_sum。)拿一个统计所有string长度的例子写代码:

 1 string::size_type 
 2 stringLengthSum(string::size_type sumSoFar, const string& s)
 3 {
 4     return sumSoFar + s.size();
 5 }
 6 set<string> ss; // 建立字符串的容器,
 7 ... // 进行一些操作
 8 string::size_type lengthSum = // 把lengthSum设为对
 9     accumulate(ss.begin(), ss.end(), // ss中的每个元素调用
10         0, stringLengthSum); // stringLengthSum的结果,使用0作为初始统计值

  不过,运算类型若改变比较类中数据成员的值,有可能(事实上基本没有这种可能,考虑多线程情况下故意修改还算有可能)造成未定义错误。

  所以如果要改变类中数据成员的值for_each就排上了用场,for_each带有一个区间和一个函数(一般是一个函数对象)来调用区间中的每个元素,但传给for_each的函数只接收一个实参(当前的区间元素),而且当完成时for_each返回它的函数。

条款38:把仿函数类设计为用于值传递

  C和C++都不允许你真的把函数作为参数传递给其他函数。取而代之的是,你必须传指针给函数。函数指针是值传递。STL函数对象在函数指针之后成型,所以STL中的习惯是当传给函数和从函数返回时函数对象也是值传递的(也就是拷贝)。函数对象以值传递和返回,你的任务就是确保当那么传递(也就是拷贝)时你的函数对象行为良好。这暗示了两个东西。第一,你的函数对象应该很小。否则它们的拷贝会很昂贵。第二,你的函数对象必须单态(也就是,非多态)——它们不能用虚函数。那是因为派生类对象以值传递代入基类类型的参数会造成切割问题:在拷贝时,它们的派生部分被删除。

  禁止多态仿函数是不切实际的,如果你想要建立一个包含很多数据的多态仿函数类,应该先建立一个包含一个指向实现类的指针的小而单态的类,然后把所有数据和虚函数放到实现类,即pimpl模式。

条款39:用纯函数做判断式

  判断式是返回bool(或者其他可以隐式转化为bool的东西)。纯函数是返回值只依赖于参数的函数。一个判断式类是一个仿函数类,它的operator()函数是一个判断式。判断式中采用值传递方式,并不对传入参数进行修改,所以要在外部传参确定对象是否满足判断式要求,否则可能因为参数问题而导致误操作。

条款40:使仿函数类可适配

  ptr_fun做的唯一的事是使一些typedef有效。提供这些必要的typedef的函数对象称为可适配的,而缺乏那些typedef的函数对象不可适配。

  typedef是从一个基结构中继承,仿函数类中operator(),必须继承自模板unary_function和binary_function。STL暗中假设每个仿函数类只有一个operator()函数,而且这个函数的参数和返回类型要被传给unary_function或binary_function,不能通过建立一个单独的含有两个operator()函数的struct试图组合功能。如果你那么做了,这个仿函数可能可以和最多一种它的调用形式(你传参数给binary_function的那个)适配,而一个只能一半适配的仿函数可能只比完全不能适配要好。

TBC

 

 

 

---恢复内容结束---

posted on 2017-08-30 17:41  Hello_Motty  阅读(210)  评论(0编辑  收藏  举报