9.1、顺序容器概述
包括 vector、deque、list、forward_list、array、string //其中forward_list、array为c++11的新特性
另外还有3种适配器,stack、queue、priority_queue;
9.2、迭代器
#include "stdafx.h"
#include <iostream>
#include<forward_list>
#include<vector>
#include<array>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{<pre class="cpp" name="code"> array<int,5> a ={0,1,2,3};//后面自动补0
vector<int>v(a.begin(),a.end());
//vector<int>v={0,1,2};//error
v.push_back(0);
v.push_back(1);
v.push_back(2);
for(auto iter=v.begin();iter<v.end();iter++)
cout<<*iter;
cout<<endl;
vector<int>v1(v);
for(auto iter=v1.rbegin();iter<v1.rend();iter++)
cout<<*iter;
return 0;
}
输出:
01230012 21003210请按任意键继续. . .
思想:
这种反向迭代思想不错,利用别的容器来进行复制构造,但是还是不能像array那样进行初始化。
1、assign和swap
assign适用于初始化之后,再用别的容器或者常量来对其赋值
如:v.assigh(c.begin(),c.end())
或者(10,8)//10个8
v1.assign(v.begin(),v.begin()+3);//这里为从begin开始数3个字符,这是为了跟end()配合
swap
swap(v1,v);//还是很好用的
9.3、顺序容器操作
1、向顺序容器添加元素
(1)array都不支持
(2)vector,string支持push_back(),不建议使用insert
(3)deque支持头尾插
(4)insert(pos,v.begin(),v.end());//在插入位置插入数,返回插入位置
或者insert(插入位置,{...})//返回刚刚指向
(5)现在还没有找到emplace和push_back等的区别,都调用的是复制构造函数
2、访问元素
*iter front() at() []
3、删除元素
pop_back() erase () (begin,end) clear
#include "stdafx.h"
#include <iostream>
#include<string>
#include<vector>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> v(3,2);
v[1]=3;
v[2]=4;
for(auto iter=v.begin();iter<v.end();iter++)
if ( *iter == 3)
{ iter=v.erase(iter);//这里erase函数改变了iter的值,因此需要返回iter,否则会出错<span style="font-family: Arial, Helvetica, sans-serif;"> </span>
<span style="white-space:pre"></span><pre class="cpp" name="code"><span style="white-space:pre"> </span>--iter;
cout<<*iter<<endl; //返回的位置还是原来擦除的位置}for(auto iter=v.begin();iter<v.end();iter++)cout<<*iter<<endl;return 0;}
这个是面试官问我的一个题,其实看起来简单,但实际上要操作起来,还是有问题的
输出:
424请按任意键继续. . .
思想:
(1)注意擦除之后记得要返回位置
(2)擦除函数的用法,中间用的是迭代器
4、forward_list
before_begin cbefore_begin insert_after emplace_after erase_after
、forward_list和array类型list(双向链表)和forward_list(单项链表)可以使得在任何位置插入删除都很快,但是不支持随机访问。list<int> c(ar.begin(),ar.end());c.insert(c.begin(),3); //这样3就插在了最前面,在迭代器前面插入,同样支持push_back push_frontfc.insert_after(c.begin(),3);//单项链表只支持在迭代器后插入,且支持push_front,前插法array是一种更安全更容易使用的数组类型,长度是固定的。用法为:array<int,10> ar={1,2}; 列表初始化,都是可以的,但是要vs2012及以上的编译器
5、resize
多余的会被删掉,但是不会改变容器的大小
6、管理容量的成员函数
capacity() reserve() shrink_to_fit() size()
9.4 string 的一些操作
#include "stdafx.h"
#include <iostream>
#include<string>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
char a[]="fdsljklcvtfdsfsCVTfdklkjfcVtd888";
string b="";
for(int i=0;i<sizeof(a);i++)
b+=tolower(a[i]);
string c(a);
size_t pos=0;
int count=0;
while(pos<b.size())
{
pos=b.find("cvt",pos);
if(pos<b.size())
{
c.replace(pos+count,3,"cvte");//每替换掉一个c就多出一个字符,需要进行调整
count++;
}
else
break;
pos=pos+count+3;//调整
}
cout<<c<<endl;
return 0;
}
这个是cvte的一个题,当时不知道string有这么强大的功能,这里拿来用,很轻松的就解决了问题。
思想:
(1)利用char的转化操作tolower转化小写
(2)string有find("fs",pos)在pos个字符之后找字符的功能,用while循环
(3)每替换掉一个,cvte就比以前多了个字符,所以需要向前移动一下
输出:
fdsljklcvtefdsfscvtefdklkjfcvted888
请按任意键继续. . .
1、reserve和resize的区别
reserve只是改变了capacity的大小,但是没有改变size。只有一个参数。
resize改变了capacity,也改变了size,如原来有3个元素,那么v.resize(5,2),会在原来元素的后面添加两个元素。如果只有一个参数的话,那么会调用默认的构造函数。
2、除了erase还有clear函数可以用。用来删除元素。
3、区分string中find(“”,pos)以及find_first_of(“”)的区别
find_first_of为里面任何一个字符第一次出现的位置。
find_last_of任意一个字符最后一次出现的位置
find_first_not_of第一个不在里面的字符
find_last_not_of最后一个不再里面的字符
4、emplace操作
emplace_front、emplace、emplace_back
分别对应push_front、insert、push_back区别是,emplace是构造而不是拷贝对象。
如一个对象为salse,共有三个元素。那么用法为:
c.emplace("aaa",1,2);
c.insert(salse("aaa",1,2));
10.1 一些算法
#include "stdafx.h"
#include <iostream>
#include<string>
#include<vector>
#include<list>
#include<iterator>
#include<algorithm>
#include<array>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> v;
fill_n(back_inserter(v),10,1);//作为初始化之后要填充还是有用处的
fill(v.begin(),v.end(),3);//当v为空时会出现问题
replace(v.begin(),v.end(),3,2);//替换
array<int,4> a={1,2,3};
//sort和unique操作
v.clear();
v.push_back(0);
v.push_back(2);
v.push_back(3);
v.push_back(2);
v.push_back(3);
sort(v.begin(),v.end());
auto end_iter=unique(v.begin(),v.end());//返回排序消除重复的元素的最后一个位置,且会覆盖掉重复的元素,那么整个容器变得不可信
for(auto iter=v.begin();iter<end_iter;iter++)
cout<<*iter<<endl;
//输出0 2 3
return 0;
}
思想:
(1)注意fill操作不能允许为空,但是用back_inserter 还是可以的
(2)注意sort作用还是蛮大的,另外unique是返回一个迭代器的。
10.3 定制操作
1、lambda表达式
1.1 绑定,形参,可修改捕获值、修改返回类型等
适用于只在一两个地方使用的简单操作
绑定
int sz=4;
auto f=[&sz](int a,int b){return (a+b)>sz?a+b:sz;};//放在主函数内,感觉这个东西还是蛮有用的
sz=5;
cout<<f(1,2)<<endl;//其中sz必须是函数体内的一个局部变量,这样用这种lambda还是不错的
输出:5
因为对捕获值进行绑定 &sz
反之没有绑定的话,输出4
可修改
int sz=4;
auto f=[sz](int a,int b)mutable{return (a+b)>sz?a+b:++sz;};//放在主函数内,感觉这个东西还是蛮有用的
sz=5;
cout<<f(1,2)<<endl;//其中sz必须是函数体内的一个局部变量,这样用这种lambda还是不错的
输出:5
在没有绑定的情况下,要修改sz的值,需要加上mutable,然而若为绑定,则不需要
修改返回类型
int sz=4;
auto f=[sz](int a,int b)mutable -> int{if((a+b)>sz)return a+b;else return ++sz;};//放在主函数内,感觉这个东西还是蛮有用的
sz=5;
cout<<f(1,2)<<endl;//其中sz必须是函数体内的一个局部变量,这样用这种lambda还是不错的
输出:5
lambda一般是不允许返回两种类型的,会默认为void,如果非要那么做,必须加上-> int 其中int为返回类型。
一些泛型算法
#include <iostream>
#include<vector>
#include<string>
#include<algorithm>
#include<iterator>
using namespace std;
int main()
{
int a[5]={1,3,2,3,4};
vector<int> v(a,a+5);
vector<int> vtemp;
replace(v.begin(),v.end(),2,5);
//替换操作非常强大,但是这种方法不适用于整个文本中子串的替换吧
sort(v.begin(),v.end());//排序也挺方便
for_each(v.begin(),v.end(),[](const int &a){cout<<a<<" ";});//以后就用这种输出方式,显的很好用
cout<<endl;
auto iter=unique(v.begin(),v.end());//去掉重复,返回最后不重复元素的下一个
for_each(v.begin(),v.end(),[](const int &a){cout<<a<<" ";});
cout<<endl;
v.erase(iter,v.end());//擦除重复的
for_each(v.begin(),v.end(),[](const int &a){cout<<a<<" ";});
cout<<endl;
iter=find_if(v.begin(),v.end(),[](const int &a)-> bool{if(a>=4)return true;else return false;});//最后一个参数是判断
//可以简写为[](const int &a){return a>=4 ;}
for_each(iter,v.end(),[](const int &a){cout<<a<<" ";});
system("pause");
return 0;
}
1、find(v.begin().v.end(),val)若找到则指向它,否则指向end.
find_if(v.begin().v.end(),[](const int &a){return true;}) //根据后面的lambda表达式来判断。
2、accumulate(v.begin().v.end(),val),是由第三个参数决定初始值,以及做什么操作,以及返回什么类型,可以是string,int等,但是如果类型是double,但是初值为0,那么最后返回的是整形。
3、equal(v.begin(),v.end(),v1.begin())比较两个容器中的元素,判断是否相等。第二个容器中的元素至少和第一个容器中的元素一样多。
4、fill(v.begin(),v.end(),val)是覆盖操作
fill_n(v.begin(),n,val) 从这个位置开始,后面的n个元素覆盖为val,但是会出问题,因为原来本来就没有这么多元素。
因此用到auto it=back_inserter(v);其返回一个迭代器it,当执行*it=val的时候,会调用push_back将元素插入。
用法为fill_n(back_inserter(v),n,val) ,会默认调用push_back。
5、copy
数组拷贝如:copy(begin(a1),end(a1),a2),其中a1和a2都是数组。
6、replace()
7、删除重复单词
sort(v.begin(),v.end());//可以重载其排序方法
auto iter=unique(v.begin(),v.end());
v.erase(iter,v.end());
8、字符串排序
elimDups(words);//按照字典序排序,消除重复
stable_sort(words.begin().words.end(),[](const string &s1,const string &s2){return s1.size()<s2.size();})//按照长度排序,但是长度相同的话按照字典序排序。
注意后面用的是lambda表达式。
9、lambda表达式的应用场合
在函数比较短而且只在一两个场合使用的地方。
10、bind在旧版本中是用bind1st和bind2st分别绑定第一个和第二个参数的,但是现在被启用,用bind非常方便。
int f(int, char, double);
// 绑定f()函数调用的第二个和第三个参数,
// 返回一个新的函数对象为ff,它只带有一个int类型的参数
auto ff = bind(f, _1, ‘c’, 1.2); 其中_1为指定传进来参数的位置。
int x = ff(7);
也就是说可以更改位置:如bind(f,_3,_2,_1)等。
绑定引用参数
bind是做拷贝的,因此对于大的对象如ostream,需要做引用处理,因此需要用ref函数,其作用是返回一个对象的引用
添加头文件<functional>
using namespace std::placeholders//因为_1等都在std::placeholders命名空间中。
其实可以用下面这种方法,就不用ostream了。
void print(const int &a)
{
cout<<a<<" "<<endl;
}
for_each(v.begin(),v.end(),bind(print,_1));
如果非要用的话
ostream &print(ostream &out,const int &a)
{
return out<<a<<" "<<endl;
}
for_each(v.begin(),v,end(),bind(print,ref(cout),_1))
也是可以的。
11、插入迭代器 back_inserter、front_inserter、inserter??
12、iostream迭代器??
13、反向迭代器
14、特定容器算法??
11.1 使用关联容器
1、set
#include "stdafx.h"
#include <iostream>
#include <set>
using namespace std;
int main()
{
int a[] = {1,2,3};
set<int> s(a,a+3);
set<int>::iterator iter;//不能用auto
for(auto iter=s.begin();iter!=s.end();iter++)
cout<<*iter<<" ";
cout<<endl;
if((iter=s.find(1))!=s.end());//无法find string
cout<<*++iter;
int b;
cin>>b;
while(cin.get()!='\n')//不为为回车,get()函数直接转化成ASCII码,但是会删去最后一个元素,不好用
{
s.insert(b);
cin>>b;
}
return 0;
}
思想:
(1)在find函数中 iter不能用auto,遍历的时候可以
(2)对于string类型的set无法用find函数
(3)利用cin.get()来判断是否输入回车,但是这种办法的缺点是,最后一个字符不能插入,这个问题有待解决
2、map
#include "stdafx.h"
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main()
{
map<int, string> mapStudent;//pair<int,string>p;p=make_pair(v1,v2);<SPAN style="BACKGROUND-COLOR: rgb(240,248,255); FONT-FAMILY: Arial; COLOR: rgb(255,0,0); FONT-SIZE: 13px"> </SPAN>
mapStudent.insert(pair<int, string>(1, "student_one"));
mapStudent.insert(pair<int, string>(2, "student_two"));
mapStudent.insert(pair<int, string>(3, "student_three"));
map<int, string>::iterator iter;
for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
{
cout<<iter->first<<" "<<iter->second<<endl;
}
}
输出:
1 student_one
2 student_two
3 student_three
请按任意键继续. . .
思想:
(1)发现一个问题,就是无法用string作为第一个,这样会出现问题,无法输出。如果两者不能都用整形,那么这个实际上有什么用处。
赋值和遍历操作
#include "stdafx.h"
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main()
{
map<string,size_t> m;
m["aa"]=1;
cout<<m["aa"]<<" "<<m["bb"]<<endl;
for(auto iter=m.begin();iter!=m.end();iter++)
cout<<iter->first<<iter->second;
}
思想:
(1)第一个参数为关键字,第二个为值key-value
(2)遍历为利用iter指向来读取,还是比较方便的
(3)进行查询的时候若找不到关键字,则将关键字加入,默认初始化。
1、介绍
map 关键数组,保存关键字-值对
set 关键字即值,即只保存关键字的容器
multimap 关键字可重复出现
multiset 同样
无序
unordered_map//以哈希函数组织的map
unordered_set
unordered_multimap
unordered_set