C++11学习笔记 9.docx
- 文档编号:10063188
- 上传时间:2023-05-23
- 格式:DOCX
- 页数:14
- 大小:21.94KB
C++11学习笔记 9.docx
《C++11学习笔记 9.docx》由会员分享,可在线阅读,更多相关《C++11学习笔记 9.docx(14页珍藏版)》请在冰点文库上搜索。
C++11学习笔记9
大多数算法都是定义在头文件algorithm中。
标准库还在头文件numeric中定义了一组数值泛型算法
算法不依赖于容器,但依赖于元素类型
泛型算法本身不会执行容器的操作,它们只会运行于迭代器之上,执行迭代器的操作。
因此,算法不会改变底层容器的大小。
find(bigin(ia),end(ia),val);
intsum=accumulate(vec.cbegin(),vec.cend(),0);
这条语句将sum设置为vec中元素的和,和的初始值为0,第三个参数说明了函数中使用哪个加法运算符以及返回值得类型。
vec中的元素必须可以是int,或者是double,longlong或者其他任何可以加到int上的类型,
stringsum=accumulate(v.cbegin(),b.cend(),string("")); ///正确
stringsum=sccumulate(v.cbegin(),b.cend(),"")//错误,constchar*上么有定义+运算符
另一个只读算法equal(),用于确定两个序列是否保存相同的值。
equal(roster1.cbegin(),roster1.cend(),roster2.cbegin());
两个容器的元素不必一样,只要可以用==比较。
而且有一个假设,第二个序列至少与第一个序列一样长。
写入元素:
fill(vec.begin(),vec.end(),0);
vector
fill_n(vec.begin(),10,0); // 灾难,修改vec中不存在的元素
fill_n(vec.begin(),vec.size(),0);//将所有元素置零
插入迭代器back_inserter,定义在头文件iterator中的一个函数。
接受一个指向容器额引用。
返回一个与该容器绑定的插入迭代器。
vector
autoit=back_inserter(vec);
*it=42;
vector
//空向量
fill_n(back_inserter(vec),10,0);////正确,back_inserter创建一个插入迭代器,可用来添加元素
int a1[]={0,1,2,3,4,5,6,7,8,9};
int a2[sizeof(a1)/sizeof(*a1)];//a2 和a1大小一样
// ret指向拷贝到a2的尾元素之后的位置
autoret=copy(begin(a1),end(a1),a2);
前两个迭代器是被拷贝元素范围,第三个是目的序列的其实位置
replace(ilst.begin(),ilst.end(),0,42);//将所有的0都替换为42;
replace_copy(ilst.cbegin(),ilist.cend(),back_inserter(ievc),0,42);
ilist中的元素不变,ivec包含ilst的一份拷贝,不过原来在ilst中的值为0的元素用42替换
sort用元素类型的<来实现排序
重排容器元素的算法:
voidelimDups(vector
{
sort(words.begin(),words.end());
autoend_unique=unique(words.begin(),words.end());//将重复元素移到末尾,并返回最后一个不重复元素之后的位置
words.erase(end_unique,words.end());//删除元素
}
标准库算法对迭代器而不是容器进行操作。
因此,算法不能(直接)添加或删除元素。
删除是调用成员函数
若果使用sort时,比较元素么有定义< 我们可以使用谓词:
一个可调用的表达式,其返回结果是一个能用作条件的值
一元谓词,意味着只接受单一参数,二元谓词,元素类型必须可以转换成谓词的参数,定义的是元素之间的一种关系
boolisShorter(conststring&s1,conststring&s2)
{
returns1.size() } sort(words.begin(),words.end(),isShorter); 通常我们不在意排序时相等元素的相对位置,但是我们可以通过stable_sort这种稳定的排序算法来排序,保证排序后相等元素相对位置不变 elimDups(words); stable_sort(words.begin(),words.end(),isShorter); for(constauto&s: words) cout< cout< find_if: 接受一对迭代器,表示一个范围,第三个参数是一个谓词。 find_if对输入序列中的每个元素调用给定的谓词。 它返回第一个使谓词返回非0值得元素,如果不存在则返回尾迭代器,find_if接受的一元谓词 对于一个对象或表达式,如果可以对其使用调用运算符,则称它为可调用的。 如果e是一个可调用的表达式,则我们可以编写代码e(args),args是用逗号分隔的参数列表 可调用的对象: 函数,函数指针,重载了函数调用符的类,lambda表达式 一个lambda表达式表示一个可调用的代码单元。 我们可以将其理解为一个未命名的内联函数。 [capaturelist](parameter)->returntype{functionbody} capturelist(捕获列表)是一个lambda所在函数中定义的局部变量的列表(通常为空),lambda表达式必须使用尾置返回 autof=[]{return42};//我们定义了可调用对象f,不接受参数,返回42; cout< 如果忽略返回类型,函数体中只有一个return,lambda根据函数中的代码推断出返回类型, 如果lambda的函数体包含任何单一return语句之外的内容,且未指定返回类型,则返回void lambda不能有默认参数,因此一个lambda调用的实参数目永远和形参数目相等 空的捕获列表表示lambda不使用它所在函数中的任何局部变量 一个lambda表达式通过局部变量包含在捕获列表中来指出将会使用这些变量。 [sz](conststring&a) {returena.size()>=sz;}; //注意,花括号后的分号! autowc=find_if( words.begin(),words.end(),[sz](conststring&a){returna.size()>=sz;} ) for_each(wc,words.end(),[](conststring&a){cont< cout< 完整的biggles: stringmake_plural(size_tctr,conststring&word,conststring&ending) { return(ctr>1)? word+ending: word; } voidbiggies(vector : size_typesz) { elimDups(words); //将words按字典顺序排序,删除重复单词 stable_sort(words.begin(),words.end(),[](conststring&a,conststring&b) {returna.size() autowc=find_if(words.begin(),words.end(),[sz](conststring&a), [sz](conststring&a) {returna.size()>=sz;});//获得一个迭代器,指向第一个满足size()>=sz的元素 autocount=words.end()-wc; cout< <<"oflength"< for_each(wc,words.end(),[](conststring&s){cout< cout< } 当定义一个lambda时,编译器会生成以个与lambda对应的新的(未命名)类类型。 当使用lambda初始化的变量时,定义一个从lambda生成的对象。 默认情况,从lambda生成的类包含一个对应这个lambda所捕获的变量的数据成员,类似普通成员,lambda的数据成员也在lambda对象被创建时被初始化 捕获变量的值在lambda创建时拷贝,因而随后对变量的修改不会影响到对应lambda内对应的值 autof2=[&v1]{returnv1;};//返回的是v1指向的对象的值 io对象不可拷贝,所以对于io对象来说,我们就可以用引用来传递了。 特别的,对于捕获引用来说,必须保证引用是存在的,且有预期的值 一般来说,我们应该尽量减少捕获的数量,来避免潜在的捕获导致的问题,而且,如果可能的话,应该避免捕获指针或引用 隐式捕获,除了显示的列出希望使用的来自所在函数的变量之外,我们也可以让编辑器根据lambda中的代码来推测, &捕获引用;=值捕获 //sz采用隐式捕获,值捕获方式 wc=find_if(words.begin(),words.end(),[=](conststring&s){returns.size()>=sz;); 如果我们希望对一部分变量采用值捕获,对其他变量采用引用捕获,可以混合使用隐式捕获和显示捕获 for_each(words.begin(),words.end(),[&,c](conststring&s){os< 当我们混合使用了隐式捕获和显示捕获时,捕获列表中的第一个元素必须是一个&或=。 此符号指定了默认的捕获方式 但使用混合捕获方式时,显示与隐式必须采用不同的捕获方式。 如,如果隐式捕获采用引用方式,则显式捕获命名变量必须采用值得方式 默认情况下,对于一个被拷贝的变量,lambda不会改变其值,若果要改变被捕获的值,就必须在参数列表首加上mutable,因此可以省略参数列表 autof=[v1]()mutable{return++v1;}; 一个引用捕获的变量是否可以修改依赖于此引用指向的是一个const还是非const类型 autof2=[&v1]{return++v1;}; 默认情况下,如果一个lambda体包含return之外的任何语句,则编译器假定返回void。 就是没有返回值、 transform(vi.begin(),vi.begin(),vi.begin(),[](inti){returni<0? -i: i;});//将序列中的每一个负数替换成绝对值。 前两个迭代器是输入范围,第三个迭代器是目的位置,我们无须指定返回类型,可以根据条件运算符的类型推断出来 transform(vi.begin(),vi.end(),vi.begin(),[](inti){if(i<0)return-i;elsereturni;});//错误 编辑器推断这个版本的lambda返回类型为void,但返回一个int,此时我们就好指定返回类型 transform(vi.begin(),vi.end(),vi.begin(),[](inti)->int{if(i<0)return-i;elsereturni;}); 对于那种只要一两个地方使用的简单操作,lambda表达式是最有用的,若果在多个地方,或这比较复杂,通常用的是函数 如果lambda表达式的捕获列表是空的,通常可以用函数来代替 定义在functional头文件中的bind函数: 可以看做是一个通用的函数适配器,它接受一个可调用的对象,生成一个新的可调用对象“适应”源对象的参数列表,autonewCallable=bind(callable,arg_list) 当我们调用newCallable时,newCallable会调用callable,并传递它arg_list中的参数 arg_list中的参数可能使_n.n是整数。 这些参数是占位符,数值n表示的是生成可调用对象的位置: _1为newCallable的第一个参数,依次类推 autowc=find_if(words.bengin(),words,end(),bind(check_size,_1,sz); _n定义在placeholders的命名空间中,而这个命名空间本身定义在std命名空间中。 _1的声明为 usingstd: : placeholders: : _1; 与bind函数一样,palceholders命名空间也定义在functional头文件中 用bind重排参数顺序 sort(words.begin(),words.end(),isShorter);///按单词长度由短到长排序 sort(words.begin(),words.end(),bind(isShorter,_2,_1));///由长到短 默认情况下,bind的那些不是占位符的参数被拷贝到bind返回的可调用对象中,但是我们可以使用引用 for_each(words.begin(),words.end(),[&os,c](conststring&s){os< ostream&print(ostream&os,conststring&s,charc) { returnos< } for_each(words.begin(),words.end(),bind(print,os,_1,''));//错,不能拷贝os for_each(words.begin(),words.end(),bind(print,ref(os),_1,'')); ref返回的一个对象,包含给定的引用,此对象可以拷贝。 标准库中还有一个cref函数,生成的是const引用的类。 这两个函数在functional头文件中(老版c++用的是bind1st和bind2nd,已经被弃用) 除了为每个容器定义的迭代器之外,标准库的iterator头文件中还定义了额外的几种迭代器 插入迭代器: 这些迭代器被绑定到一个容器上,可用来向容器插入元素 流迭代器: 这些迭代器被绑定到输入或输出流上,可用来遍历所关联额IO流 反向迭代器: 这些迭代器向后而不是向前移动,除了forward_list之外的标准库容器都有反向迭代器。 移动迭代器: 这些专用的迭代器不是拷贝其中的元素,而是移动它们 插入迭代器是一种迭代器适配器,它接受一个容器,生成一个迭代器,能实现向给定容器添加元素。 it=t; 在it指定的当前位置插入t。 假定c是it绑定的容器,依赖于插入迭代器的不同种类,此赋值会分别调用c.push_back(t) c.push_front(t) c.insert(t,p),其中p为传递给inserter的迭代器位置 *it ,++it,it++ 这些操作虽然存在,但不会对it做任何事情。 每个操作都返回it 插入迭代器有三种,差异在于元素的插入位置 back_inserter 创建一个使用push_back的迭代器 front_inserter 创建一个使用push_front的迭代器 insert 创建一个使用insert的迭代器。 此函数接受第二个参数,这个参数必须是一个指向给定容器的迭代器,元素被插入到给定迭代器所表示的元素之前 只有在容器支持对应操作的情况下,我们才能使用对应的迭代器 调用insert(c,iter)时,得到一个迭代器,接下来使用它会将元素插入iter原来所指向的元素之前的位置。 *it=val; 其效果与下面代码一样: it=c.insert(it,val);//it指向新插入的元素 ++it; //递增it使他指向原来元素 当我们使用front_inserter时,元素总是插入到容器第一个元素之前。 而对于inserter指向第一个元素,只要我们插入新元素,此元素就不是首元素了,当调用front_inserter(c),我们得到一个插入迭代器,接下来会调用push_front。 当每个元素被插入c中时,它变为首元素,可见,front_inserter生成的迭代器会将插入的元素序列顺序颠倒过来,而inserter和back_inserter则不会 istream_iterator 读取输入流,ostream_iterator 向一个输出流写数据 当创建一个流迭代器时,必须指定迭代器将要读写对象的类型,一个istream_iterator使用>>来读取流,因此该类型必须定义了输入允许符。 创建时,我们可以将它绑定到一个流上,也可以默认初始化,来创建一个可以当作尾后值使用的迭代器 istream_iterator istream_iterator while(int_it! =int_eof) //当有数据可供读取时执行,一旦其关联的流遇到文件尾或遇到IO错误,迭代器的值就与尾后迭代器相同 vec.push_back(*in_iter++); //存入vector 使用一对表示范围的迭代器来构造vector: istream_iterator vector in_iter 返回从流中读取的值 in->men 与(*in).men含义相同 使用算法: istream_iterator cout< istream_iterator绑定到一个流时,标准库不保证迭代器立即从流读取数据,保证的是,在我们第一次解引用迭代器之前,从流中读取数据的操作已经完成。 一般情况无所谓,如果我们创建了一个istream_iterator,么有使用就销毁了,或者我们正在从两个不同的对象同步读取同一个流,那么何时读取就很重要了 我们可以对任何带有输出运算符的类型定义ostream_iterator。 创建时我们可以提供第二个参数,一个c风格的字符串。 在输出每个元素后都会打印此字符串。 必须将ostream_iterator绑定到一个流上,不允许空的 out=val; 用<<运算符将val写入到out所绑定的ostream中。 val的类型必须与out可写的类型兼容 *out ++out out++这些运算符值存在的,但是不对out做任何事情,每个运算符都返回out ostream_iterator for(autoe: vec) *out_iter++=e; cout< 值得注意的是,当我们向out_iter赋值时,可以忽略解引用可递增运算。 循环写成 for(autoe: vec) out_iter=e; cout< 也可以通过copy类打印: copy(vec.begin(),vec.end(),out_iter); cout< 除了forward_list之外,其他容器都支持反向迭代器。 我们可以通过调用rbegin、rend、crbegin、crend成员函数来获得反向迭代器。 返回的是指向容器尾元素和首元素之前一个位置的迭代器。 sort(vec.begin(),vec.end());//按正常顺序 sort(vec.rbegin().vec.rend());//逆序 因为流迭代器不支持递减运算,因为不可能在一个流中反向移动。 因此不能砸iforward_list和流迭代器创建反向迭代器 反向迭代器的目的是表示元素的范围,而这些范围是不对称的,导致一个结果: 当我们从一个普通迭代器初始化一个反向迭代器,或是给反向迭代器赋值,结果迭代器与原迭代器指向的不是相同的元素 五类迭代器: 输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器 除了输出迭代器之外,一个高层类别的迭代器支持低层类别迭代器的所有操作 (对于传递错误类别迭代器,很多编译器不会给出任何警告或提示) find算法在一个序列上进行一遍扫描,对元素进行只读操作,至少需要输入迭代器 replace函数需要一对迭代器,至少是前向迭代器。 replace_copy前两个迭代器参数要去至少是前向迭代器。 其第三个表示目的位置,必须至少是输出迭代器。 输入迭代器: 可以读取序列中的元素。 必须支持: == ! = ++ * -> 输入迭代器只用于顺序访问,*it++保证是有效的,但递增科恩能够导致所有其他指向流的迭代器失效,因此,输入迭代器只能用于单遍扫描的算法。 find和accumulate 输出迭代器,必须支持: ++ * 只能向输出迭代器赋值一次,而且只能用于单遍扫描的算法。 用作目的位置的迭代器通常就是输出迭代器,如,copy的第三个参数 前向迭代器: 支持输入输出的说有操作,使用前向迭代器可以进行多遍扫描,replace要求的就是前向迭代器,forward_list上的就是 双向迭代器,支持前向迭代器的所有操作,还支持递减运算符(--)reverse要去的就是双向迭代器 随机访问迭代器,sort要求随机访问,array、deque、string、vector的迭代器都是随机访问迭代器 使用谓词的算法 unique(beg,end); unique(beg,end,comp); _if版本: find(beg,end,val)//查找输入范围中val第一次出现的位置 find(beg,end,pred);//查找第一个令pred为真的元素 默认情况,重排算法将重排后的元素写回给定的输入序列中,这些算法还提供了另一个版本,将元素写到一个指定的输出目的位置。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+11学习笔记 C+ 11 学习 笔记