高效使用 STL 的 11 个条款

仅仅是个选择的问题,都是STL,可能写出来的效率相差几倍;
熟悉如下条款,高效的使用STL;算法

当对象很大时,创建指针的容器而不是对象的容器

  1. STL基于拷贝的方式的来工做,任何须要放入STL中的元素,都会被复制;
    这也好理解,STL工做的容器是在堆内开辟的一块新空间,而咱们本身的变量通常存放在函数栈或另外一块堆空间中;为了可以彻底控制STL本身的元素,为了能在本身的地盘随心干活;这就涉及到复制;
    而若是复制的对象很大,由复制带来的性能代价也不小 ;
    对于大对象的操做,使用指针来代替对象能消除这方面的代价;编程

  2. 只涉及到指针拷贝操做, 没有额外类的构造函数和赋值构造函数的调用;函数

    vecttor <BigObj> vt1;
    vt1.push_bach(myBigObj);
    
    vecttor <BigObj* > vt2;
    vt2.push_bach(new BigObj());

注意事项:工具

  1. 容器销毁前须要自行销毁指针所指向的对象;不然就形成了内存泄漏;
  2. 使用排序等算法时,须要构造基于对象的比较函数,若是使用默认的比较函数,其结果是基于指针大小的比较,而不是对象的比较;

用empty() 代替size()来检查是否为空

由于对于list,size()会遍历每个元素来肯定大小,时间复杂度 o(n),线性时间;而empty老是保证常数时间;性能

尽可能用区间成员函数代替单元素操做

使用区间成员函数有如下好处:指针

  1. 更少的函数调用
  2. 更少的元素移动
  3. 更少的内存分配

例:将v2后半部的元素赋值给v1:code

单元式操做:server

for (vector<Widget>::const_iterator ci = v2.begin() + v2.size() / 2;
ci != v2.end();
++ci)
v1.push_back(*ci)

使用区间成员函数assign():对象

v1.assign(v2.begin() + v2.size() / 2, v2.end());

使用reserver避免没必要要的内存分配(for vector)

新增元素空间不够时,vector会进行以下操做:blog

  1. 分配当前空间的两倍空间;
  2. 将当前元素拷贝到新的空间中;
  3. 释放以前的空间;
  4. 将新值放入新空间指定位置;

若是预先知道空间的大小,预先分配了空间避免了从新分配空间和复制的代价;
注:reserve()只是修改了容量,并不是大小,向vector中增长元素仍是须要经过push_back加入;

使用有序的vector代替关联容器(阶段性的操做适用)

对阶段性操做的定义:
先作一系列插入、完成以后,后续操做都是查询;

在阶段性的操做下,使用vector有如下优点:

  1. 由于vector有序,关联容器带来的有序优点散失;
  2. 都是使用二分法查找的前提下,查询算法对连续的内存空间的访问要快于离散的空间;

在map的insert()和operator[]中仔细选择

插入时,insert效率高;由于operator会先探查是否存在这个元素,若是不存在就构造一个临时的,而后才涉及到赋值,多了一个临时对象的构造;

更新时,[]效率更高,insert会创造一个对象,而后覆盖一个原有对象;而[]是在原有的对象上直接赋值操做;

散列函数的默认比较函数是equal_to,由于不须要保持有序;

尽可能用算法替代手写的循环

  1. 效率相比手写更高;
    STL的代码都是C++专家写出来的,专家写出来的代码在效率上很难超越;
    除非咱们放弃了某些特性来知足特定的需求,可能能快过stl;好比,基于特定场合下的编程,放弃通用性,可移植性;
  2. 不容易出错;
  3. 使用高层次思惟编程
    相比汇编而言,C是高级语言;一条C语言语句,用汇编写须要好几条;
    一样的,在STL的世界中,咱们也有高层次的术语:
    高层次的术语:insert/find/for_each(STL算法)
    低层次的词汇:for /while(C++语法)
    用高层次术语来思考编程,会更简单;

尽可能用成员函数代替同名的算法

  1. 基于效率考虑,成员函数知道本身这个容器和其余容器有哪些特有属性,可以利用到这些特性;而通用算法不能够;
  2. 对于关联容器,成员函数find基于等价搜索;而通用算法find基于相等来搜索;可能致使结果不同;

使用函数对象代替裸函数做为算法的输入参数

由于内联,在函数对象的方式中,内联有效,而做为函数指针时,通常编译器都不会内联函数指针指向的函数;即便指定了inline;
好比:

inline bool doubleGreater(double d1, double d2)
{
    return dl > d2;
}
vector<double> v;
...
sort(v.begin(), v.end(), doubleGreater);

这个调用不是真的把doubleGreater传给sort,它传了一个doubleGreater的指针。
更好的方式是使用函数对象:

sort(v.begin(), v.end(), greater<double>())

注:《effcient c++》中的实验结论,使用函数对象通常是裸函数的1.5倍,最多能快2倍多

选择合适的排序算法

须要排序前思考咱们的必要需求,可能咱们只是须要前多少个元素,这时并不须要使用sort这种线性时间的工具,性能消耗更少的parttition多是更好的选择;

如下算法的效率从左到右依次递减:

partition > stable_partition / nth_element / patical_sort / sort / stable_sort

功能说明:

  • partition :将集合分隔为知足和不知足某个标准两个区间;
  • stable_partition :partition的稳定版本;
  • nth_element :获取任意顺序的前N个元素;
  • patical_sort :获取前N个元素,这个N个元素已排序;
  • sort:排序整个区间;
  • stable_sort:sort的稳定版本;

选择合适的容器

为何vector不提供push_front()成员方法?由于效率太差,若是有太多从前面插入的需求,就不该该使用vector,而用list;
关心查找速度,首先应该考虑散列容器(非标准STL容器,如:unordered_map,unordered_set);其次是排序的vector,而后是标准的关联容器;

参考

《effictive STL》
《Efficient C++》

推荐支持

若是你以为本文对你有所帮助,请点击如下【推荐】按钮, 让更多人阅读;


Posted by: 大CC | 25JUN,2015
博客:blog.me115.com [订阅]
微博:新浪微博

相关文章
相关标签/搜索