STL又叫标准模板库,提供各种容器。
STL是C++一部分,不休要额外安装什么,它被内建在编译器之内。
STL重要特点是,数据结构和实现分离。
*所谓迭代器,类似一个游标,使用++指向下一个元素,使用--指向前一个元素。实际上“迭代器”是一个对象,封装了指针,重载了操作符。
1. 动态数组,可变数组
#include<vector>//存放容器的
#include<algorithm>//STL的算法
void CallBackPrintVector(int v)//因为知道迭代器是vector<int>,所以形参为int
{
cout<<v<<end;
}
int main()
{
vecotor<int> v;
v.push_back(10);//添加元素
//通过STL提供的算法,进行遍历。容器提供迭代器,迭代器作为for_each参数。v.begin()提供一个开始的迭代器。
vector<int> ::iterator pBegin = v.begin();// vector<int> ::iterator可能是内部类。begin是指向第一个元素
vector<int> ::iterator pEnd = v.end();//end是指向最后一个元素,再后一个位
for_each(pBegin , pEnd , CallBackPrintVector) ;
//for_each其实是一个算法, 传入beign和end,实质上是为了判断什么时候结束迭代。传入回调函数,是每次迭代都触发。
//也可以:
for(vector<int> ::iterator it=pBegin; it!=pEnd; it++)
{
CallBackPrintVector(*it);//应该是重写了*号操作符。
}
}
for_each还可以:
class/struct Op{ void operator()(int a) ;}
for_each(pBegin , pEnd , Op()) ;//传入重写()操作符的Op类的匿名对象
//
反向迭代器 v.rbegin(),v.rend()
//
vector实现动态增长:如果空间不足,会重新申请更大的内存,将原空间的数据拷贝到新空间,再将新元素插入。所以叫动态数组。
//
v.size();//返回元素个数;
//
v.resize(10,(重载)多出来部分的默认值);//如果容器变短,那么超出的元素被剔除;
//
v.empty();//判断是否为空;
//
v.capacity();//获取容量,根据插入原理,每次都拷贝数据到新空间,会很低效,所以有大于size的容量;
//
vector支持随机访问,意思是,v.insert(v.begin()+2,100);//v.begin()+2就是随机访问,可以任意跳。其他容器就不一定了,可能只能进行++操作
//
vector在删除的时候,并不会自动收缩内存区域,所以要 verctor<int>(v).swap(v) ,以收缩空间。首先,vector<int>(v),是以v来实例化一个匿名的对象,长队和v一样,然后vector<int>(v).swap(v),将匿名对象的指针,和v交换。也可以v.resize()
//
v.reserve(10000);//在调用push_back前预留空间,这样子的话,就不需要在空间不够的时候,重新开辟,然后拷贝了。只是开辟内存空间,没有初始化的。
//
2. string容器
char *是指针,string是类
string封装了char*,管理这个字符串,是一个char*容器
不用考虑内存释放,越界
int main()
{
string s1;
string s2(10,'a');
string s3("absdfasdf");
string s4(s3);
for(int i=0;i<s1.size();i++)
{
s1[i];//如果用中括号形式,访问越界,直接挂了,没有办法捕获异常。重写了[]符号
s1.at(i);//访问越界,会抛出异常 out_of_range
}
}
find()从前往后查找。int index= find("aaa");
rfind()从后先前查找
//字符串比较
if(s1.compare(s2)==0)
{
//相等
}
//字符串擦除
s.erase(0,2);
//char*转string
int main(){
char *s = "aaa";
string sstr(s);
}
//string转char*
int main(){
string str ="aaa";
const char* cstr = str.c_str();
}
3. Deque容器
在头部和尾部插入,删除,都很快的容器。
可随机存取,效率高。(应该是连续的内存空间)
.push_back():从尾部插入
.pop_back():从尾部删除
.push_front():从头部插入
.pop_front():从头部删除
.begin() 头部迭代器
.front():返回第一个元素
d1.swap(d2):交换两个空间的元素
#include<deque>
int main()
{
deque<int> d1;
deque<int> d2(10,5);//用10个5初始化
deque<int> d3(d2.begin(),de.end());
deque<int> d4(d3);
for(deque<int>::iterator it=d4.begin();it!=d4.end();it++)
{
cout<< *it;
}
cout<<endl;
}
**排序函数
void print(int e);
int main()
{
deque<int> de;
de.push_back(10);
de.push_back(11);
//...
sort(de.begin(),de.end());//排序,应该是跟for_each,是一个在deque头文件中声明的函数
for_each(de.begin(),de.end(),print);
de.pop_front();//删除第一个元素
de.pon_back();//删除最后一个元素
}
4. 对于sort的特别使用
class Person
{
public:
int age;
string Name;
Person(int age,char * Name);
}
//排序规则
bool compareCallblack(Person &p1, Person&p2)
{
return p1.age < p2.age;//这里决定是正序还是倒序,<是从小到大排序
}
int main()
{
Person p1(1,"tom"),p2(2,"marry");
Vector ps ;
ps.push_back(p1);
ps.push_back(p2);
sort(ps.begin(),ps.end(),compareCallblack);
}
5. stack 容器,先进后出的容器
push():压栈
pop():出栈,删除最后入栈的
top():获取栈顶元素
注意:栈不能遍历,不提供迭代器,更不支持随机存取
#include<int>
int main()
{
stack<int> s1;
stack<int> s2(s1);
s1.push(10);
s1.push(20);
s1.push(30);//栈顶
s1.pop();//删除栈顶元素
while(!s1.empty())
{
cout<<s1.top();
s1.pop();
}
}
6. queue容器
先进先出,在一端插入,另一端出
push():入队操作
pop():删除队头,即删除最先如队的
front():队头元素,即最先入队的
注意:队列不能遍历,不提供迭代器,更不支持随机存取
int main()
{
queue<int> q;
q.push(1);
q.push(2);
q.push(3);
q.push(4);
while(q.size()>0)
{
cout<<q.front();
q.pop();
}
}
//输出1234
7. List容器
链表,包含数据域和指针域,链表内存结构是非连续的
添加删除元素,时间复杂度都是常数,不需要移动数据。
链表的内存没有像vector一样,有所预留。
提供迭代器。
List不支持随机访问,一般sort都要支持随机访问的,所以,List自己提供sort算法。
int main()
{
list<int> mlist;
list<int> milist2(10,10);
list<int> mlist3(mlist2.begin(),mlist2.end());
//
mlist.insert(mlist.begin(),200);//在从头插入
mlist.insert(mlist.end(),200);//在尾插入
//
mlist.pop_back();//删除最后一个
mlist.remove(200);//能去除所有200的值,应该有重载,能调像sort一样,删除符合条件的值
//
for(list<int>::iterator it=mlist3.begin();it!=mlist3.end();it++)
{
cout<< *it;
}
}
mlist.push_back();//往后插入
mlist.push_front();//往前插入
mlist.sort(compareCallblack);//排序
8. set容器
//是基于二叉树算法的容器,二叉树是用于查找数据的一种优化算法
set只提供insert()方法,因为是自动排序的,所以没有其他方法添加数据
set不能插入重复元素
set要改变元素的值,只能先删除,再插入
int mian()
{
set<int > s1;
s1.insert(1);
//...
for(set<int >::itertor it=s1.begin();it!=s1.end();it++)
{
cout<s1;
}
}
注意:无论按什么顺序插入,set会根据算法自动排序,所以按顺序插入是没有意义的。而且其遍历方法,分为先序,中序,后序方法。
s.erase(value);//删除
s.erase(s.begin());//
multset容器可以放重复元素,这是与set唯一的区别
//查找
set只有实只(map有key和value(实)值)
s.find(key);//查找key是否存在,如果存在,返回该元素的迭代器,否则,返回s.end(); 注意: end是指向比最后一个元素还要后的位置的
int main()
{
set<int > s;
set<int>::interator ret= s.find(4);
if(ret==s.end())
{
cout<<"not found"<<endl;
}
//查找大于
set<int> ::interator s1.lower_bound(2);//找第一个小于等于其值的
set<int> ::interator s1.upper_bound(2);//找第一个大于等于其值的
pair< set<int>::iterartor, set<int>::iterartor> pairIterator = s1.equal_range(2);//返回upper_bound和lower_bound的结果
pairIterator .first;
pairIterator .secord;
if(pairIterator .first==s1.end())
{}
}
set制定一个排序规则
例一:
//C++坑真多,和sort又不一样了
class mycmopare
{
public :
bool operator()(inv v1,int v2)
{
return v1>v2;
}
}
int main()
{
set<int ,mycmopare> s1;//估计里面自行实例化一个对象,然后调用operator排序
s1.insert(7);
s1.insert(6);
}
例二:
class person
{
public:
int age;
string Name;
person(int age,string name);
}
//仿函数
class mycompare
{
bool operator()(person &p1, person&p2)
{
return p1.age>p2.age;
}
}
int main()
{
ser<person,mycompare> sp;//set需要排序,当放对象前,需要告知其排序准则,所以在构造前要模板形式传入
person p1=person(10,"a"),p2=person(1,"b")
sp.insert(p1);
sp.insert(p2);
for(set<person,mycompare>::iterator it=sp.begin();it!=sp.end();ip++)
{
cout<<(*sp).age<<endl;
}
//
p3 = person(1,"c")
sp.find(p3);//这样是可以查到的,因为是根据mycompare重载()操作符函数,是以age排序的。
}
9. Map容器
Map会根据Key自动排序,Map封装了pair类;
不支持随机访问
#include<map>
int main()
{
map<int , int > mymap;
pair<map<int,int>::iterator,bool> ret = mymap.insert(pair<int,int>(1,1)); //创建匿名的pair<int,int>对象放进去
//返回迭代器,以及是否成功的pair对象
if(ret->second)
{
cout<<"插入成功"<<endl;
}
mymap.insert(make_pair(2,1));
mymap.insert(map<int,int>::value_type(3,1));
mymap[40]=40;//第四种构造方式,如果key不存在,那么就插入pair,如果Key存在,就修改value值。如果cout<<mymap[40]<<endl,key不存在的话,也会插入,并赋一个默认值。
for(map<int,int>::iterator it = mymap.begin();it!=mymap.end();i++)
{
cout<<"key:"<<(*it).first<<"value:"<<it->second;
}
}
//
例二:
class person
{
public :
string Name;
person(string name):Name(name);
}
struct mycompare//可以写成类,偷懒就写struct
{
bool operator()(person &p1,person &p2)
{
return p1.Name!=p2.Name;
}
}
int main()
{
map<person,int,mycompare> mymap; //由于要自动排序,自定义的数据类型,要告知其排序规则
mymap.insert(make_pair(person("tom"),20));
}
10. multimap
//multimap的key可以相同,而且相同key的键值,放在连续的存储空间
//类似于部门和员工,Key是部门id
.count(key);//返回key的元素个数
int main()
{
multimap<int , Worker> workerGroup;
workerGroup.insert(make_pair(1,Worker("张三")));
//按组打印
multlmap<int,worker> ::iterator it = workerGroup.find(1);//返回针对一个Key的迭代器,迭代下去,能遍历这个key的所有value
int count = workerGroup.count(1);
for(int i=0;i<count;i++,it++)
{
cout<<it->second.workername<<endl;
}
}
11. 函数元素的深拷贝和浅拷贝
元素是如何放进容器中的?用引用,还是浅拷贝,还是深拷贝?
如果元素存在指针成员,就涉及浅拷贝问题。
元素放到容器中,是进行浅拷贝的,所以,放到容器中的元素,要重写拷贝构造函数、析构函数。
12. 函数对象,仿函数
class A
{
public : bool operator (const int &a);
}
int main()
{
A a;
a();//仿函数调用,容器也是这个原理,那么就不需要使用函数指针了
}
13. 绑定适配器
#include <vector>
#include<algorithm>
#include<functional>
struct P
{
void operator()(int v)
{}
}
int main()
{
verctor<int> a;
a.push_back(1);
//..
for_each(a.being(),a.end(),P());//传入一个重写()操作符的匿名对象
}
//但是,因为operator只能传一个参数,而且这个参数是for_each内赋值的,要想传多一个参数,那么:
struct Pp:public binary_function<int,int,void> //这里第一个int应该是int v中的 ,第二个int是 inv val中的,第三个,应void operator()的void
{
void operator()(int v,int val)
{}
}
//
for_each(a.being(),a.end(), bind2nd( P() , 1000 ));
//绑定适配器,将一个一元函数对象,变为二元的
猜测:
struct bind2nd
{
bind2nd(binary_function<int,int,void> p, int val);
binary_function<int,int,void> P;
int val;
void operator()(int v)
{
return P(v,val);
}
}
14. 取反适配器
not2
struct cmp
{
bool operator()(int a,int b)
{
return a>b;
}
}
void main()
{
vector<int> v;
v.push_back(1);
//...
sort(v.begin(),v.end(),cmp());//不传cmp()匿名对象,那么就按升序排了。
}
//使用适配器 not2
struct cmp2:public binary_function<int,int,bool>
{
bool operator()(int a,int b)
{
return a>b;
}
}
sort(v.begin(),b.end(),not2(cmp2)); //这里的效果是,对cmp2的结果,再取反,又变回从小到大了。
取反适配器not1,针对一元对象
struct cmp3
{
bool operator()(int a)
{
return a>5;
}
}
vector<int>::iterator it = find_if(v.begin(),v.end(),cmp3());//返回大于5的
//
struct cmp4: public unary_fuction<int,bool>
{
bool operator()(int a)
{
return a>5;
}
}
//
vector<int>::iterator it = find_if(v.begin(),v.end(),not1(cmp4()));//此时返回小于5的
//
注意,判断方法:
if(it==v.end())
{
cout<<"not found"<<endl;
}
15. 函数对象适配器
ptr_func
void fun1(int v)
{
}
void fun2(int v,int val)
{
}
int main()
{
vector<int> v;
//..
for_each(v.begin(),v.end(), ptr_func( fun1));//不传对象也行,传函数名
//
for_each(v.begin(),v.end(), bind2nd( ptr_func( fun2), 10));//不传对象也行,传函数名
}
16. 成员函数适配器
mem_fun_ref(存放的对象),mem_fun(存放的是指针)
如果容器中,存放的是对象,那么for_each中,希望调用类的方法
class person
{
public:
string name;
void show();//不需要传参,因为本身就带person *const this;
}
void main()
{
vecotor<person> v;
v.push_back(person("zhangsan"));
//..
for_each(v.begin(),v.end(),mem_fun_ref(&Person::show));
}
//如果放的是对象指针
void main()
{
vecotor<person*> v;
v.push_back(&(person("zhangsan")));
//..
for_each(v.begin(),v.end(),mem_fun(&Person::show));
}
17. 其他函数
find
void int main()
{
vector<int> v;
v.push_bakc(10);
//..
vector<int>::iterator ret = find(v.begin(),v.end(),8);
if(ret!=v.end()) { cout<<"found"<<endl;}
//
}
//
class person
{
public:
int id;
int age;
person(int id,int age) :id(id),age(age);
bool operator ==(person &p) const // ==必须重载,因为find里面,将匿名对象作为const引用,并调用==操作符重载方法。(const对象,成员方法必须标记为const)
{
return p.id==this->id;
}
}
int main()
{
vecotor<person> v1;
v1.push_back(person(1,2));
v1.push_back(person(2,2));
verctor<person> ::iterator ret = find(v.begin(),v.end(),p1); //
}
binary_serch 二分查找法
返回bool类型,只能告知有没有找到
int main()
{
vector<int> v;
v.push_back(1);
//..
bool ret = binary_serch(v.begin(),v.end(),1);
//
}
adiacent_find
查找相邻重复的元素迭代器
vector<int> :: iterator it=adiacent_find(v.begin(),v.end());
find_if
根据条件,查找第一个
bool cp(int val)
{
return val > 3;
}
vector<int>::iterator = find_if(v.begin(),v.end(),cp);//估计传过去,就F()这样的,或TT++这样,*TT这样,直接调传过去的()、++、*这样的操作符函数。
count_if
int num = count_if(v.begin(),v.end(),cp);