Effective STL
第9条:慎重选择删除元素的方法
删除特定值元素,vector、string、deque用erase-remove:c.erase(remove(c.begin(),c.end(),1963),c.end());
list用c.remove();关联容器用c.erase().
vector、string、deque删除特定位置元素,用erase返回被删除元素下一位置的迭代器,因此用i=c.erase(i);
关联容器删除特定位置元素,则用c.erase(i++)。
第14条:使用reserve避免不必要的内存分配
size()容器多少元素;capacity()能容纳元素总数;resize()(Container::size_type n)强迫容器改变到包含n个元素的状态,n比当前size小,则尾部元素会被析构,大则通过默认构造函数创建新元素并加到末尾;reserve(Container::size_type n)把容器容量变为至少n。
第17条:使用swap技巧除去多余容量
vector<Contestant>(contestants).swap(contestants);创建一个临时矢量,它是contestants的副本,然后和contestants中数据做swap操作。
同样对string也适用:string(s).swap(s);
swap操作时,迭代器、指针、引用也被交换,因此原迭代器、指针、引用依然有效,只不过在另一容器中了。
第21条:总是让比较函数在等值情况下返回false
第22条:切勿直接修改set或multiset中的键
要修改set中的键值时,使用const_cast<E&>(*i).setTitle("New Title"),而不是static_cast<E>(*i).setTitle("New Title"),因为后者转换的结果是一个临时的匿名对象。
第24条:当效率至关重要时,请在map::operator[]与map::insert之间谨慎做出选择
添加元素时用insert,更新元素值时用operator[]。
第27条:使用distance和advance将容器的const_iterator转换为iterator
通过const_iterator得到iterator的一个方法:advance(i,distance<const_iterator>(i,ci));首先创建一个新的iterator i,指向容器起始位置,
然后计算两个迭代器(它们指向同一个容器)之间的距离,然后移动i使它与const_iterator指向相同元素。
第31条:了解各种与排序有关的选择
完全排序sort、stable_sort
只要求对前n个排序partial_sort
只找最前n个,但不对这前n个排序nth_element
标准序列容器中元素按照是否满足某条件区分开来,用partition和stable_partition
list有自己的sort算法,但由于没有随机访问迭代器,因此不能用partial_sort、nth_element等算法。
第32条:如果确实需要删除元素,则需要在remove这一类算法之后调用erase
remove不删除元素,只是把元素值用后面的元素覆盖,因此要真正删除需要使用容器的成员函数erase(remove接受迭代器作为参数,不接受容器作为参数,因此不知道它操作的是什么容器)
v.erase(remove(v.begin(),v.end(),99),v.end())
list的remove函数是个例外,它结合了remove和erase,因此会真正删除元素。
与remove类似的算法还有remove_if和unique。
第34条:了解哪些算法要求使用排序的区间作为参数
用于查找的算法binary_search、lower_bound、upper_bound、和equal_range要求排序的区间,因为它们使用二分法查找数据。它们对数时间的查找效率,但如果不是随机访问迭代器,则不能保证。
unique和unique_copy不要排序区间,因为它们只删除连续相等的元素只保留一个。
标准STL没有包含copy_if算法。
第37条:使用accumulate或者for_each进行区间统计
accumulate(v.begin(),v.end(),0.0);
accumulate(v.begin(),v.end(),1.0f,multiplies<float>());
Point avg=for_each(v.begin(),v.end(),PointAverage()).result();
for_each执行完毕后返回它的函数(在上一行的例子中会返回PointAverage(),因此结果可以访问它的result()成员函数)。
accumulate绝不仅仅是将几个数加在一起求合那么简单,可以自定义运算子。相比之下,for_each稍显繁琐但更灵活。原因在于 accumulate返回的是size_type,而for_each返回Function。
第38条:遵循按值传递的原则来设计函数子类
template<class InputIterator,class Function> Function //返回值类型为Function for_each(InputIterator first, InputIterator last, Function f); // 注意值传递
在STL中,函数对象在函数之间来回传递的时候也是像函数指针那样按值传递的。因此,你的函数对象必须尽可能的小,否则拷贝的开销会很大;其次,函数对象必须是单态的,也就是说,它们不得使用虚函数。这是因为,如果参数的类型是基类类型,而实参是派生类对象,那么在传递过程中会产生剥离问题(slicing problem):在对象拷贝过程中,派生部分可能会被去掉,而仅保留了基类部分(见第3条)。
试图禁止多态的函数子同样也是不实际的。所以必须找到一种两全其美的办法,既允许函数对象可以很大并且/或保留多态性,又可以与STL所采用的按值传递函数子的习惯保持一致。这个办法就是:将所需要的数据和虚函数从函数子中分离出来,放到一个新的类中,然后在函数子中设一个指针,指向这个新类。
第39条:确保判别式是“纯函数”
有几个名词可以来解释一下
■ 判别式(predicate)返回bool类型的函数
■ 纯函数(pure fuction)指返回值仅与输入有关
■ 判别式类 一个函数子类,它的operator()函数是一个判别式
问题根源在于:在一个算法中,你无法确定函数子被调用多少次。像for_each这类简单的算法调用次数就是迭代器的个数,但其它的较复杂的就不一定了,比如find_if就不一样。设计函数功能单一是一个重要的原则。
第40条:若一个类是函数子,则应使它可配接
总共有4个配接器not1, not2, bind1st, bind2nd(它们要求一些特殊的类型定义,argument_type、first_argument_type、second_argument_type、result_type)。如果你要使用它们,你必须确保函数子类从unary_function, binary_function等继承。或者使用ptr_fun, mem_fun等(这个用起来不太方便)。对于前一种方法,有一个使用特点
1、对于无状态函数子类(指没有私有成员变量的),通常定义为struct;这是STL的风格,当然用class也完全一样。
2、一般情况下,传递给unary_function或binary_function的非指针类型需要去掉const和引用(&)部分。(作者不愿意解释其中的原因,但我的编译器加上它们后也没错)
struct Cmp: public std::binary_function<int, int, bool> {
bool operator()(const int& i, const int& j);
}
如果是指针,就要写成一致
struct Cmp: public std::binary_function<const int*, const int*, bool> {
bool operator()(const int* i, const int* j);
}
其实写函数子也不是很麻烦
第41条:理解ptr_fun、mem_fun和mem_fun_ref的由来
用一句话来说就是“函数调用方式的多样性,使用STL必须规定一种方式来约束”。
C++三种不同的调用语法:f(x)、x.f()、p->f(),其中后两种是成员函数形式,而STL总是使用第一种,也就是非成员函数的语法形式。因此需要ptr_fun、mem_fun和mem_fun_ref进行配接。
其中,mem_fun用于指针容器,也就是将第三种转为第一种,它的具体声明为:
template<typename R,typename C> //C是类,R是所指向的成员函数的返回类型
mem_fun_t<R,C>
mem_fun(R(C::*pmf)());
因此调用时for_each(v.begin(),v.end(),mem_fun(&Widget::test)),for_each接收到一个类型为mem_fun_t的对象,该对象中保存了一个指向Widget::test的指针,对容器v中每一个Widget*指针,for_each使用语法1(非成员函数形式)调用mem_fun_t对象,然后该对象使用语法3调用Widget*指针的Widget::test()。
而mem_fun_ref用于对象容器。
ptr_fun的说明从另一个博客摘录如下:
ptr_fun是将一个普通的函数适配成一个仿函数(functor), 添加上argument_type和result type等类型,它的定义如下:
template<class _Arg1,
class _Arg2,
class _Result> inline
pointer_to_binary_function<_Arg1, _Arg2, _Result,
_Result(__clrcall *)(_Arg1, _Arg2)>
ptr_fun(_Result (__clrcall *_Left)(_Arg1, _Arg2))
{ // return pointer_to_binary_function functor adapter
return (pointer_to_binary_function<_Arg1, _Arg2, _Result,
_Result (__clrcall *)(_Arg1, _Arg2)>(_Left));
}
下面的例子就是说明了使用ptr_fun将普通函数(两个参数, 如果有多个参数, 要改用boost::bind)适配成bind1st或bind2nd能够使用的functor,否则对bind1st或bind2nd直接绑定普通函数,则编译出错。
#include <algorithm>
#include <functional>
#include <iostream> using namespace std; int sum(int arg1, int arg2)
{
std::cout<< "arg1 = " << arg1 << std::endl;
std::cout<< "arg2 = " << arg2 << std::endl; int sum = arg1 + arg2;
std::cout << "sum = " << sum << std::endl; return sum;
} int main(int argc, char *argv[], char *env[])
{
bind1st(ptr_fun(sum), )(); // the same as sum(1,2)
bind2nd(ptr_fun(sum), )(); // the same as sum(2,1) return ;
}
第42条:确保less<T>与operator<具有相同的语义
在排序容器中,有一个默认的比较函数子less<T>,见第20条。确保比较函数子可行性是很重要的,同时,为了避免出现混乱,less<T> (如果需要自己实现,就不能用less这个名字)与operator<不同会有歧义。
第46条:考虑使用函数对象而不是函数作为STL算法的参数
C/C++不能真正将函数作为参数,而传递的函数指针,因此没法实现内联,而用函数对象作为参数,虽然有实例化然后调用operator()的开销,但可以实现内联(显式inline或者定义在类的内部即隐式内联),因此效率更高。例如C++的sort就性能而言优于C的qsort。
Effective STL的更多相关文章
- 容器使用的12条军规——《Effective+STL中文版》试读
容器使用的12条军规——<Effective+STL中文版>试读 还 记的自己早年在学校学习c++的时候,老师根本就没有讲STL,导致了自己后来跟人说 起会C++的时候总是被鄙视, ...
- Effective STL中文版 译序
<Effective STL中文版>译序 就像本书的前两本姊妹作(Effective C++.More Effective C++)一样,本书的侧重点仍然在于提升读者的经验,只不过这次将焦 ...
- 《Effective STL中文版》前言
<Effective STL中文版>前言 我第一次写关于STL(Standard Template Library,标准模板库)的介绍是在1995 年,当时我在More Effec ...
- Effective STL 中文版(大全)
Effective STL 中文版(大全) 作者:winter 候捷说,对于STL,程序员有三个境界,开始是使用STL,然后是理解STL,最后是补充STL.Effective STL是一本非常好的书, ...
- [置顶] Effective STL 学习笔记
看Effective STL 作的一些笔记,希望对各位有帮助. 以下是50条条款及相关解释. 容器 1. 慎重选择容器类型,根据需要选择高效的容器类型. 2. 不要试图编写独立于容器类型的代码. 3. ...
- C++之Effective STL
今天看了下websocket的知识,了解到这是html5新增的特性,主要用于实时web的通信.之前客户端获取服务端的数据,是通过客户端发出请求,服务端进行响应的模式,或者通过ajax每隔一段时间从后台 ...
- Effective STL 读书笔记
Effective STL 读书笔记 标签(空格分隔): 未分类 慎重选择容器类型 标准STL序列容器: vector.string.deque和list(双向列表). 标准STL管理容器: set. ...
- Effective STL读书笔记
Effective STL 读书笔记 本篇文字用于总结在阅读<Effective STL>时的笔记心得,只记录书上描写的,但自己尚未熟练掌握的知识点,不记录通用.常识类的知识点. STL按 ...
- Effective STL 43: Prefer algorithm calls to hand-written loops
Effective STL 43: Prefer algorithm calls to hand-written loops */--> div.org-src-container { font ...
- Effective STL 学习笔记 39 ~ 41
Effective STL 学习笔记 39 ~ 41 */--> div.org-src-container { font-size: 85%; font-family: monospace; ...
随机推荐
- mysql修改表名,列名,列类型,添加表列,删除表列
alter table test rename test1; --修改表名 ); --添加表列 alter table test drop column name; --删除表列 ) --修改表列类型 ...
- UIControl事件
1.UIControlEventTouchDown 单点触摸按下事件:用户点触屏幕,或者又有新手指落下的时候. 2.UIControlEventTouchDownRepeat 多点触摸按下事件,点触计 ...
- Fiddler 的Window 8.1中不可以正常工作
昨天晚上新升级了操作系统至Windows 8.1 Pro,发现使用Fiddler时网站不能正常使用,导致“代理错误”等信息. 解决方案: http://fiddler2.com/blog/blog/2 ...
- fifo manage
typedef struct { TEntry *header; TEntry *tailer; uint16 alloc; uint16 release;}TPoolCtrl;
- Lintcode: Kth Smallest Number in Sorted Matrix
Find the kth smallest number in at row and column sorted matrix. Example Given k = 4 and a matrix: [ ...
- flot_js_$用法解释
$用法解释 $在JS中本身只是一个符号而异,在JS里什么也不是.但在JS应用库JQUERY的作者将之做为一个自定义函数名了,这个函数是获取指定网页元素的函数,使用非常之频繁,所以好多新手不知道,还以为 ...
- 关于MVC4中EFCoderFirst 数据迁移的三句经典指令
首先输入这句指令 enable-migrations -contexttypename SchoolContext ---------(SchoolContext为你设置的数据库名)它会自动 ...
- 夺命雷公狗ThinkPHP项目之----企业网站21之网站前台二级分类显示名称(TP自定义函数展示无限极分类)
我们实现网站二级分类的显示的时候,先要考虑的是直接取出顶级栏目,控制器代码如下所示: <?php namespace Home\Controller; use Think\Controller; ...
- 夺命雷公狗---DEDECMS----1dedecms的安装过程
我们这次要玩的dedecms(cms也就是内容管理系统),电商网.或者政府网..小说网.新闻网之类的都是基于可以用cms来实现的. 现在在市场上主流的cms系统有dedecms(织梦),帝国cms,p ...
- 夺命雷公狗---微信开发57----微网站之jquery_mobile之入门案例
这节课我们主要用到到jquery_mobile来实现一个点电影播放网站 jquery_mobile(简称JQM)其实就是基于jquery开发出来的一套移动端框架,适应移动用户端市场对浏览与体验从而进一 ...