###学习《C++ Primer》- 3
点击查看Evernote原文。
#@author: gr
#@date: 2014-10-04
#@email: forgerui@gmail.com
Part 3: STL泛型算法(第10章)
一、算法永远不会执行容器的操作
算法本身不会执行容器的操作,它们只会运行在迭代器上,执行迭代器的操作,使用迭代器,使算法更加通用。算法的编程假定:算法永远不会改变底层容器的大小。算法可能改变元素的值或移动元素,但永远不会直接添加或删除。
还有一个inserter的迭代器,可以向容器中添加元素。
二、只读算法
accumulate进行累加操作,可以进行数值累加,也可以进行字符串相加(连接)。
accumulate(c.cbegin(), c.cend(), string(""));
equal比较两个容器的元素是否一致,前两个表示容器的范围,第三个表示第二个容器的首位置。要保证第二个序列至少和第一个序列一样长。
equal(c1.cbegin(), c1.cend(), c2.cbegin());
三、写容器元素的算法
fill和fill_n不改变容器的大小,所以要保证有足够的空间。
vector<int> vec(10);
fill(vec.begin(), vec.end(), 0); //fill(begin, end, val);
fill_n(vec.begin(), vec.size(), 0); //fill_n(pos, n, val);
vector<int> vec2;
fill_n(vec2.begin(), 10, 0); //出错,vec2是空的,无法写入10个元素
back_inserter是插入迭代器,通过这个迭代器进行赋值时,系统会自动调用push_back函数。
vector<int> vec;
auto it = back_inserter(vec);
//进行赋值,会自动调用push_back进行插入
*it = 42;
fill_n(back_inserter(vec), 10, 1);
copy向目的地写入元素。
copy(vec1.cbegin(), vec2.cend(), back_inserter(vec2));
replace替换元素值。
//把0替换为42
replace(vec.begin(), vec.end(), 0, 42);
replace_copy接受第三个迭代器,指出调整后序列的保存位置。
replace_copy(vec.cbegin(), vec.cend(), back_inserter(vec2), 0, 42);
四、重排容器元素的算法
unique并没有真正删除重复的元素,只是将重复的元素排到最后,end()成员函数返回的迭代器位置不变,调用erase删除之后,才真正删除重复的元素。
vector<string> words = {"fox", "the", "red", "jump"}
//排序
sort(words.begin(), words.end());
//排列在范围的前部,返回不重复区域之后的一个位置迭代器
auto end_unique = unique(words.begin(), words.end());
//删除重复单词
words.erase(end_unique, words.end());
五、lambda表达式
[capture list] (parameter list) -> return type { function body }
可以忽略参数列表和返回类型,但必须永远包含**捕获列表**和**函数体**。
auto f = [] {return 42;} ;
//比较两个长度大小
[](const string& a, const string& b){ return a.size() < b.size(); }
下面利用这个`lambda`表达式传递给算法进行排序。
stable_sort(words.begin(), words.end(),
[](const string& a, const string& b)
{ return a.size() < b.size(); } );
使用捕获列表可以获取当前的局部变量,可以在`lambda`表达式中直接使用这个变量。
[sz](const string& a){
return a.size() >= sz;
};
#### 六、变量的修改对lambda表达式的影响
**捕获的类型**:1.值捕获;2.引用捕获;3.隐式捕获。
值捕获:被捕获的值是在lambda创建时拷贝的,随后修改捕获的局部变量值不会影响到lambda中的值。
值捕获:引用捕获则与值捕获相反,修改的值会作用到lambda表达式内部。
size_t v1 =42;
auto f1 = [v1]{return v1;} //值捕获
auto f2 = [&v1]{return v1;} //引用捕获
v1 = 0;
auto j = f1(); //值捕获,结果为42
auto k = f2(); //引用捕获,结果为0
隐式捕获:让编译器根据`lambda`表达式中的代码推断我们需要使用哪些变量。为了表示是隐式捕获,在捕获列表中加入&或=。&表示引用捕获,=表示值捕获。
wc = find_if(words.begin(), words.end(),
[=](const string& s){return s.size > sz;} );
#### 七、lambda表达式中修改变量值
一般lambda表达式不会改变值捕获中被拷贝变量的值,如果我们希望在lambda式中改变其值,则需要将表达式声明为mutable。
size_t v1 = 42;
auto f = [v1] () mutable {return ++v1;};
v1 = 0;
auto j = f(); //j为43
引用捕获是否能够修改取决于该引用是否是`const`类型。
size_t v1 = 42;
auto f = [&v1]{return ++v1;}; //v1不是const
v1 = 0;
auto j = f2(); //j为1
#### 八、lambda表达式返回类型
一些表达式无须指定返回类型,因为可以根据条件运算符的类型推断出来。如果,需要为一个lambda表达式定义返回类型时,必须使用尾置返回类型。
transform(vi.begin(), vi.end(), vi.begin(),
[](int i)-> int //指定返回类型为int
{ if (i < 0) return -i; else return i; });
#### 九、参数绑定
`bind`的一般形式:
auto newCallable = bind(callable, arg_list);
`arg_list`中的参数可能包含形如_n的名字,n是一个整数,这些参数是“点位符”,表示第几个参数:_1表示第一个参数,_2表示第二个参数。
find_if(words.begin(), words.end(),
bind(check_size, _1, sz)); //将第二个参数绑定为sz
参数:
auto g = bind(f, a, b, _2, c, _1); //g是一个有两个参数的可调用对象
g(X, Y); //调用g(X,Y)相当于f(a, b, Y, c, X);
重排参数:
auto g = bind(f, _2, _1);
g(X, Y); //调用g(X,Y)相当于f(Y, X),正好顺序相反
#### 十、插入迭代器
对插入迭代器进行赋值等于向容器中进行写入,系统会自动调用插入函数。`inserter`,`back_inserter`,`front_inserter`也都是迭代器,这些名字是原来迭代器的别名,原来的名称太长`o_0!`。
vector<int> vi = {1, 2, 3};
auto it = back_inserter(vi); //插入迭代器
*it = 4; //vi 等于 {1, 2, 3, 4}
#### 十一、流迭代器
虽然`iostream`不是容器,但标准库定义了这些IO类型对象的迭代器,`istream_iterator`,`ostream_iterator`。
istream_iterator<int> in(cin), eof;
int sum = accumulate(in, eof, 0);
ostream_iterator<int> out_iter(cout, "\n"); //每个输出后面都加一个换行
*out_iter = sum;
#### 十二、反向迭代器
反向迭代器在查寻最后出现的元素时候非常有效,但如果直接使用反向迭代器进行输出,顺序是反过来的,可以通过迭代器的`base()`成员函数使之变为正常迭代器。
auto rcomma = find(line.crbegin(), line.crend(), ','); //反向查找逗号
cout << string(line.crbegin(), recomma) << endl; //直接使用,会使输出反过来
cout << string(rcomma.base(), line.cend()) << endl; //使用base()变成普通迭代器
#### 十三、算法形参模式
一般算法都有基本的参数形式,理解这些模式,可以更专注于算法所做的操作上。一般会有如下四种形式:
alg(begin, end, args);
alg(begin, end, dest, args);
alg(begin, end, begin2, args);
alg(begin, end, begin2, end2, args);
#### 十四、算法命名规范
1. 一些算法以重载的方式传递一个谓词
//unique的重载
unique(begin, end);
unique(begin, end, comp); //传递一个比较函数,代替默认的<或==
2. _if版本的算法
//有对值进行操作的算法,和对谓词判定的算法
find(c.begin(), c.end(), val);
find_if(c.begin(), c.end(), condi); //查找condition为真的元素
3. 区分拷贝元素版本与不拷贝版本
reverse(begin, end); //反转输入范围元素的顺序
reverse_copy(begin, end, dst); //逆序拷贝到dst,这里可以使用inserter\]
###学习《C++ Primer》- 3的更多相关文章
- 学习C++ Primer 的个人理解(一)
<C++ Primer>这本书可以说是公认的学习C++最好的书,但我觉得不是特别适合作为教材,书中内容的顺序让人有些蛋疼.我个人认为初学此书是不能跳着看的.如果急于上手的话,我更推荐< ...
- 学习C++ Primer 的个人理解(九)
这一章介绍顺序容器,在之前的第三章中,了解到的vector就属于顺序容器的一种. 一个容器就是一些特定类型对象的集合. 除了vector,还有哪些顺序容器? vector: 大小可变,随机访问的速度很 ...
- 学习C++ Primer 的个人理解(三)
第三章,主要内容是字符串和数组.感觉作者的意图是希望读者可以早一点可以写出简单的小程序,并且可以早点接触迭代器这种思想. 在我看来,这种内容的难度并不大. 对于编程来说,最重要的应该是思想,类似vec ...
- 学习C++ Primer 的个人理解(二)
本身就一定基础的读者我想变量常量这些概念应该已经不是问题了.但是本章还是有几个重点,需要特别留意一下的: 1.初始化和赋值是不同的操作 2.任何非0值都是true 3.使用新标准列表初始化,在有丢失精 ...
- 学习C++.Primer.Plus 11 使用类
1.操作符重载 重载操作符的几个限制: a) 重载的至少有一个操作数是用户定义的类型,这将防止用户为标准类型重载操作符. b) 不能违反操作符原有来的句法规则. c) ...
- 学习C++.Primer.Plus 10 对象和类
1.类的声明和定义 类的声明和定义. 类声明的格式如下: class className { private://private 是类对象的默认访问控制,因此,可以省略 data member del ...
- 学习C++.Primer.Plus 8 函数探幽
1. 内联函数 普通函数调用: 存储调用指令的地址->将函数参数复制到堆栈->跳到函数地址执行代码(返回值放到寄存器)->跳回调用指令处 2. 当代码执行时间很短,且会被大量调用的 ...
- 学习C++.Primer.Plus 7 函数
C++的返回值类型不能是数组 函数原型中的变量名相当于点位符,因此不要求提供变量名. void cheers(int); C++中不指定参数列表时就使用活力号: void saybye(...); 通 ...
- 学习C++.Primer.Plus 6 分支语句和逻辑操作符
||. &&操作符是一个顺序点 < 操作符从左向右结合 ; < age < )//17<age为true, = 1,肯定 < 27.所以为整个条件为tru ...
- 学习C++.Primer.Plus 5 循环和关系表达式
C++将赋值表达式的值定义为左侧成员的值 赋值操作符是自右向左结合的 cout.setf(ios:: boolalpha);//调用设置标记,命令cout输出true或false,而非1或0. 任何表 ...
随机推荐
- 转载Code First Migrations更新数据库架构的具体步骤
[转载] Code First Migrations更新数据库结构的具体步骤 我对 CodeFirst 的理解,与之对应的有 ModelFirst与 DatabaseFirst ,三者各有千秋,依项 ...
- 【Android】JSONArray的合并
在Android开发过程中,需要处理解析服务器JSON数据时,或需要进行两个或多个JSONArray合并操作. 比如在进行LIstView的动态更新时. 在此提供一种JSONArray合并的方法,方便 ...
- uLua学习笔记(一):uLua安装及上手
uLua下载:http://www.ulua.org/ VS2012/2013的用于编写Lua的插件:https://babelua.codeplex.com/或http://unknownworld ...
- GPS两点的距离
目前手头的一个项目要用到GPS地理定位信息,很自然的就需要知道两个地点之间的距离,于是上网找了一下,同样自然的就有一些算法贴出来..(网络真是帮了大忙,省得我再去翻几何书自己研究算法了!公式早都忘光了 ...
- 剑指OFFER之复杂链表的复制(九度OJ1524)
题目描述: 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点). 输入: 输入可能包含多个测试样例,输入以EOF结束.对于每个测试案例,输入的第一 ...
- mysql---where子查询、form子查询、exists子查询
1.什么是子查询? 当一个查询是另一个查询的条件时,称之为子查询. 2.子查询有什么好处? 子查询可以使用几个简单命令构造功能强大的复合命令. 那么,现在让我们一起来学习子查询. 3.where型的子 ...
- linux IO诊断命令集
IO.sh ##iostat是查看磁盘活动统计情况 ##显示全部设备负载情况 r/s: 每秒完毕的读 I/O 设备次数.即 rio/s:w/s: 每秒完毕的写 I/O 设备次数.即 wio/s等 io ...
- com.opensymphony.xwork2.ActionSupport类源码
version : xwork-2.1.0 /* * Copyright (c) 2002-2006 by OpenSymphony * All rights reserved. */ package ...
- iOS开发——数据持久化Swift篇&模型对象归档
模型对象归档 import UIKit class ViewController: UIViewController { @IBOutlet weak var textField: UITextFie ...
- send,recv,sendto,recvfrom
send函数 int send( SOCKET s, const char FAR *buf, int len, int flags ); 不论是客户还是server应用程序都用se ...