C++ STL库学习
概述
STL库主要分为三类,容器类,迭代器类和算法类,算法类还没有研究,主要总结下容器类和迭代器类。
其中三者的关系:容器存储数据,算法通过迭代器访问容器中的数据。三者关系如下图: 容器类主要分为顺序容器,适配器,关联容器。
顺序容器主要是vector,list和dequeue,其中vector可以通过下标访问,内存是连续的,而list和dequeue是链表,内存地址不连续。
适配器比较特殊,首先栈stack和queue的默认容器是dequeue,优先队列(堆)默认容器是vector,但可以指定一种容器作为适配器的模板类型参数,然后这种类型的容器通过适配器的接口就可以实现适配器数据结构的特性。
比如,使用vector作为stack的模板参数,这个vector也可以像栈一样使用。
说到适配器和容器,这里有一个比较明显的区别,在容器尾部插入数据一般用c.push_back(),但是对于适配器的栈,队列或者堆,就只有a.push()操作,我理解为容器因为有头尾之分,所以在这里做了区别,但对于栈,队列或者堆这种数据结构,只有区别只有进数据和出数据的区别,所以就没有push_back()操作了。另外,容器可以借助迭代器遍历,但适配器并没有迭代器,可能是后面这三种数据结构不关注遍历操作,而是更关注数据间的相对关系。
关联容器主要是pair,map,set,multiset和multimap,其中pair和map用得比较多。
迭代器
迭代器和指针类似,通过如下方式声明迭代器
vector<int> c;
vector<int>::iterator it = c.begin();
需要注意的是,迭代器也有区别,可分为单边迭代器,双边迭代器和随机迭代器。
单边迭代器只能向某一边进行++或者–操作(++iterator),双边迭代器同时支持++与–操作,但这两者的比较符只支持==操作。
随机迭代器除了支持++,–操作,还支持+n,-n操作,比较操作符同时支持<,<=,>,>=操作符。
下面是不同容器对应的迭代器:
容器 | 迭代器功能 |
---|
vector | 随机访问 | deque | 随机访问 | list | 双向 | set | 双向 | map | 双向 | stack | 不支持迭代器 | queue | 不支持迭代器 | priority_queue | 不支持迭代器 |
因此在使用迭代器遍历容器数据时,对于list,set,map的截止条件应该如下形式,否则会报错。
std::list<int> alist(3,9);
for(auto it = alist.begin();it!=alist.end();++it){
std::cout<<*it<<"\n";
}
因为双边迭代器只支持==与!=操作,而对于vector,则除了上面这种形式以外,还支持下面这种遍历形式
std::vector<int> avector(3,9);
for(auto it = avector.begin();it<avector.end();++it){
std::cout<<*it<<"\n";
}
范围for循环
范围for循环基于一个名为范围变量的量,每次拷贝容器中的元素赋值给范围变量,范围for循环不用担心访问越界的问题。
基于访问的目的,如果只是读取元素,基础数据类型赋值比引用快,大型类对象则引用比赋值快。
std::vector<int> avector(8,0);
for(auto it:avector){
std::cout<<it<<"\n";
}
或者
std::vector<int> avector(8,2);
for(const auto it:avector){
std::cout<<it<<"\n";
}
大型数据对象T的话
std::vector<T> avector(3,T);
for(const auto & it: avector){
it.print();
}
如果是需要对容器内数据进行修改,则使用引用访问,因为此时范围变量是数据成员的一个别名
std::vector<T> avector(3,T);
for(auto & it: avector){
it = T();
}
此时范围变量是数据成员的一个别名
std::vector<T> avector(3,T);
for(auto & it: avector){
it = T();
}
|