• 为了保证你在浏览本网站时有着更好的体验,建议使用类似Chrome、Firefox之类的浏览器~~
    • 如果你喜欢本站的内容何不Ctrl+D收藏一下呢,与大家一起分享各种编程知识~
    • 本网站研究机器学习、计算机视觉、模式识别~当然不局限于此,生命在于折腾,何不年轻时多折腾一下

STL高效使用注意事项

C++ admin 4年前 (2016-04-07) 1337次浏览 0个评论 扫描二维码

高效使用 STL

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

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

1)STL 基于拷贝的方式的来工作,任何需要放入 STL 中的元素,都会被复制;
这也好理解,STL 工作的容器是在堆内开辟的一块新空间,而我们自己的变量一般存放在函数栈或另一块堆空间中;为了能够完全控制 STL 自己的元素,为了能在自己的地盘随心干活;这就涉及到复制;
而如果复制的对象很大,由复制带来的性能代价也不小 ;
对于大对象的操作,使用指针来代替对象能消除这方面的代价;
2)只涉及到指针拷贝操作, 没有额外类的构造函数和赋值构造函数的调用;

1
2
3
4
5
vector  vt1;
vt1.push_back(myBigObj);
vector  vt2;
vt2.push_back(new BigObj());

注意事项:
1)容器销毁前需要自行销毁指针所指向的对象;否则就造成了内存泄漏;
2)使用排序等算法时,需要构造基于对象的比较函数,如果使用默认的比较函数,其结果是基于指针大小的比较,而不是对象的比较;

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

因为对于 list,size()会遍历每一个元素来确定大小,时间复杂度 o(n),线性时间;而 empty 总是保证常数时间;

尽量用区间成员函数代替单元素操作

使用区间成员函数有以下好处:

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

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

单元式操作:

1
2
3
4
for (vector::const_iterator ci = v2.begin() + v2.size() / 2;
ci != v2.end();
++ci)
v1.push_back(*ci)

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

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

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

新增元素空间不够时,vector 会进行如下操作:
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;

比如:

1
2
3
4
5
6
7
inline bool doubleGreater(double d1, double d2)
{
    return dl > d2;
}
vector v;
...
sort(v.begin(), v.end(), doubleGreater);

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

1
sort(v.begin(), v.end(), greater())

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

选择合适的排序算法

需要排序前思考我们的必要需求,可能我们只是需要前多少个元素,这时并不需要使用 sort 这种线性时间的工具,性能消耗更少的 parttition 可能是更好的选择;
以下算法的效率从左到右依次递减:

1
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,然后是标准的关联容器;


Deeplearn, 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明STL 高效使用注意事项
喜欢 (2)
admin
关于作者:
互联网行业码农一枚/业余铲屎官/数码影音爱好者/二次元

您必须 登录 才能发表评论!