• C++中STL常用容器的用法详解


    目录

    1. string的使用

    1.1迭代器是对容器内容遍历的一种方式

    1.2 范围for也可以遍历

    2. vector 的使用

    2.1 string 和 vector的区别:

    2.2 vector 迭代器失效总结

    2.3 vector 交换接口

    3. list的使用

    3.1 list.sort() vs 算法里面的sort()

    3.2 list中的迭代器失效

    3.3 unique结合sort的使用:去重

    4. deque的使用

    5. stack的用法

    6. queue的使用

    7. priority_queue的用法

    7.1 补充知识:仿函数


    1. string的使用

    typedef basic_string<char> string;
    实际上string是类模板库中实例化的一个类(实例化的类型为char)
    学习使用就是要学习这个类提供了哪些接口,string常用的接口如下

    1. 构造函数(无参构造、有参构造、拷贝构造、赋值= 实际上是操作符重载)
    2. 遍历方法(下标+[]其实是操作符重载、迭代器s.begin() s.end()、范围for)
    3. 增(s.push_back() 、s.append()、 s += 实际上是操作符重载、s.insert())
    4. 删(s.erase())
    5. 查(s.find()、s.rfind())
    6. 替换(s.replace())
    7. 比较(s.compare())
    8. 容量类(s.size()、s.capacity()、s.resize()、s.reserve()、)
    9. 子串(s.substr())用来提取子串。//注意比较strstr库函数,用来查找字符串中是否存在某一子串。substr是STL中string类的成员函数,strstr是C语言中的库函数

    1.1迭代器是对容器内容遍历的一种方式

    1. string s2("123434");
    2. string::iterator it = s2.begin();
    3. //string::const_iterator it = s2.cbegin();//带const的迭代器,只读不写
    4. while (it != s2.end())
    5. {
    6. *it += 1;//可写
    7. cout << *it << " ";//可读
    8. ++it;
    9. }
    10. cout << endl;

    反向迭代器:逆序遍历 

    1. string s2("123434");
    2. string::reverse_iterator rit = s2.rbegin();
    3. while (rit != s2.rend())
    4. {
    5. cout << *rit << " ";
    6. ++rit;
    7. }
    8. cout << endl;

    1.2 范围for也可以遍历

    1. string s3 = "dsssds";
    2. //for (auto& e : s3)//auto是自动类型推导,知道类型的时候可以直接写char
    3. for (char& e : s3)//写的时候要加引用
    4. {
    5. e += 1;
    6. cout << e << " ";
    7. }
    8. cout << endl;

    2. vector 的使用

    其实是一个类模板,通用数组
    常用接口如下:

    1. 遍历(下标[]、迭代器、范围for)
    2. 初始化(n个val初始化、迭代区间初始化、拷贝构造)(赋值重载=)
    3. 插入元素(push_back、insert)
    4. 删除(erase)//insert和earse要先通过find找到位置pos,注意迭代器失效的情况
    5. 查找( 没有提供成员函数接口、可以用公共接口find)
      template <class InputIterator, class T>
      InputIterator find (InputIterator first, InputIterator last, const T& val);
    6. 容量类(size、capacity、resize、reserve)
      注意resize和reserve的区别
      reserve(int len); //容器预留len个元素长度,预留位置不初始化,元素不可访问。
      resize(int num); //resize元素已经初始化了,可以访问;
    7. 交换接口(有成员函数接口swap,也有全局函数接口swap)

    2.1 string 和 vector<char>的区别:

    • string类是一个保存字符的动态数组,由于其中有一个接口c_str,转化成c语言的字符串,要以\0结尾,所以string类最后会有一个\0.
    • vector<T> 是一个保存T类型的动态数组,vector<char>也是保存字符的动态数组,但是,不会以\0结尾,不保存\0.
    1. string s("hello");
    2. vector<char> v4(s.begin(), s.end());
    3. //string和vector<char>的区别,有无\0

    2.2 vector 迭代器失效总结

    • insert(it, x),或者erase(it)之后迭代器意义变了
    • insert(it, x)之后,it变成野指针,由于增容
    1. vector<int> v;
    2. v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(6);
    3. //(1)步骤一:元素3的前面插入一个30
    4. //find提供了函数模板
    5. //template <class InputIterator, class T>
    6. //InputIterator find (InputIterator first, InputIterator last, const T& val);
    7. //找到了就返回位置,没找到就返回last迭代器
    8. vector<int>::iterator pos = find(v.begin(), v.end(), 3);
    9. if (pos != v.end()){
    10. v.insert(pos, 30);
    11. }
    12. // (2)步骤2:删除元素3
    13. //(情况一迭代器失效)pos在insert以后就失效了,此时pos的位置是30,所以要重新找3
    14. //(情况二迭代器失效)insert之后,增容了(新开辟了一段空间),原来pos位置被释放了,pos是野指针了
    15. pos = find(v.begin(), v.end(), 3);
    16. if (pos != v.end()){
    17. v.erase(pos);
    18. }

    例如:要求删除v中所有的偶数

    1. vector<int> v;
    2. v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(6);v.push_back(7);
    3. vector<int>::iterator it = v.begin();
    4. while (it != v.end())
    5. {
    6. if (*it % 2 == 0){
    7. // erase(it)以后 it失效,不能++,
    8. // ***特别注意*** :erase是有返回值的,会返回删除位置it的下一个位置
    9. it = v.erase(it);
    10. }
    11. else{
    12. ++it;
    13. }
    14. }

    2.3 vector 交换接口

    提供vector的成员函数swap

    和全局函数swap

    1. vector<int> v1;
    2. v1.push_back(1);v1.push_back(2);v1.push_back(3);
    3. vector<int> v2;
    4. v2.push_back(10);v2.push_back(20);
    5. // C++98 推荐第2个方法;C++11中都一样
    6. swap(v1, v2);//交换方法1
    7. v1.swap(v2); //交换方法2

    3. list的使用

    双向循环带头链表

    1. 遍历:(迭代器、范围for)
    2. 初始化:迭代区间构造函数list (InputIterator first, InputIterator last)
      list(n, elem)、list(const list& lt)拷贝构造、赋值重载=
    3. 容量类:list.size()、list.empty()、list.resize() 
    4. 排序:list.sort()
    5. 插入删除:list.push_back() 、list.pop_back()、list.push_front、list.pop_front()、list.insert()、list.erase()
    6. list.remove(val)//移除所有的val的元素、list.clear()清除所有元素
    7. list.unique()//去重,但是只能去连续相等元素的重,所以可以结合排序使用
    8. list.reverse()//逆置
      对比函数模板里面的reverse,void reverse (BidirectionalIterator first, BidirectionalIterator last);支持双向迭代器

    3.1 list.sort() vs 算法里面的sort()

    1. void sort();
    2. template <class RandomAccessIterator>
      void sort (RandomAccessIterator first, RandomAccessIterator last);

    常见的迭代器的分类:

    •     单向 ++  forward_list
    •     双向 ++/-- list
    •     随机 ++/--/+/- vector/string

    因为算法里的sort底层使用的是快速排序,快速排序要求容器迭代器必须得是随机迭代器(能够随机访问,比如a[i]),因为快排要三数取中优化,不支持随机访问,效率就不够了。

    但是list的迭代器不是随机的,是双向的,所以不能调用算法里面的sort,只能用自己的成员函数接口。而vector、string等的迭代器是随机的,可以支持算法里的sort

    1. int a[] = { 16, 2, 77, 29 };
    2. list<int> lt1(a, a + 4); //实际是迭代区间构造函数,list (InputIterator first, InputIterator last)
    3. //原生指针可以当做天然的迭代器,其实vector/string的迭代器也是原生指针
    4. lt1.sort();
    5. //sort(lt1.begin(), lt1.end());//这里不行,因为算法里面的sort需要随机迭代器,而list的迭代器只支持双向++或--,并不是随机的
    1. int a[] = { 16, 2, 77, 29 };
    2. vector<int> v(a, a + 4); //
    3. sort(v.begin(), v.end());//默认是升序
    4. //sort(v.begin(), v.end(), greater<int>()); //降序 原理是仿函数

    3.2 list中的迭代器失效

    list中insert不会导致迭代器失效

    erase会导致失效

    1. list<int> lt;
    2. lt.push_back(1);lt.push_back(2);lt.push_back(3);
    3. list<int>::iterator pos = find(lt.begin(), lt.end(), 3);
    4. lt.insert(pos, 30);
    5. cout << *pos << endl; //这里pos位置仍然是3
    6. // vector insert会导致pos失效,list会不会呢?不会
    7. lt.erase(pos);// vector erase会导致pos失效,list会不会呢? 会
    8. //cout << *pos << endl;//这里pos已经找不到3了

    3.3 unique结合sort的使用:去重

    1. list<int> lt;
    2. lt.push_back(1);lt.push_back(2);lt.push_back(2);lt.push_back(2);
    3. lt.push_back(3);lt.push_back(4);lt.push_back(4);lt.push_back(2);
    4. lt.sort();// 一般情况,不太建议用list的排序,效率不是很高
    5. lt.unique();//去重、只能去连续的数据的重,所以要先排序
    6. //最后元素应该为:1 2 3 4

    4. deque的使用

    deque功能上是list和vector的结合体。可以头插、尾插、又可以用下标进行随机访问
    底层是中控器,类似于动态开辟的二维数组。

    但是并不能用deque来代替vector和list
    deque适合头尾插入删除,但不适合大量的中间插入删除、也不适合大量的随机访问;
    deque一般是作为stack和queue的默认适配容器

    访问方式:

    • 随机访问dq[i]
    • 迭代器
    • 范围for

    5. stack的用法

    stack常用接口:

    • 构造函数:stack<T> stk;默认构造   stack(const stack &stk);拷贝构造
    • 赋值重载:=
    • 数据存取:push(elem);  pop();   top();
    • 容量:empty();   size(); 
    1. stack<int> st;
    2. st.push(1);st.push(2);st.push(3);st.push(4);
    3. while (!st.empty()){
    4. cout << st.top() << " ";
    5. st.pop();
    6. }
    7. // 4 3 2 1

    6. queue的使用

    queue常用接口:

    • 构造函数:默认构造、拷贝构造
    • 赋值重载:=
    • 数据存取:push(elem);   pop();   front();   back();
    • 容量:empty();   size(); 
    1. queue<int> q;
    2. q.push(1);q.push(2);q.push(3);q.push(4);
    3. while (!q.empty()){
    4. cout << q.front() << " ";
    5. q.pop();
    6. }
    7. //1 2 3 4

    7. priority_queue的用法

    优先级队列,实际上底层是一个堆,默认是大堆,访问时大到小的降序

    1. // 默认是一个大堆,默认大的优先级高(也就是堆顶top元素为最大) less
    2. priority_queue<int> pq;//打印出来为大到小降序
    3. // 怎么变成小堆,堆顶元素为最小的, greater仿函数
    4. //priority_queue<int, vector<int>, greater<int>> pq;//打印出来为小到大升序
    5. // 其中第二个参数vector<>是用来承载底层数据结构堆(heap)的容器;第三个参数less<>或者greater<>是对第一个参数的比较类,less<int>表示数字越大优先级越大,greater<int>表示数字越小,优先级越大。
    6. pq.push(2);pq.push(5);pq.push(3);pq.push(1);
    7. while (!pq.empty()){
    8. cout << pq.top() << " ";
    9. pq.pop();
    10. }
    11. cout << endl;

    7.1 补充知识:仿函数

    仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。

    1. struct LessInt//仿函数
    2. {
    3. bool operator()(int l, int r) //类里面对函数调用运算符重载了
    4. {
    5. return l < r;
    6. }
    7. };
    8. bool func(int l, int r)//这是正常函数
    9. {
    10. return l < r;
    11. }
    12. int main(){
    13. LessInt le;
    14. cout << le.operator()(1, 3) << endl;
    15. cout << le(1, 3) << endl;//类对象可以像函数一样使用
    16. cout << LessInt()(1, 3) << endl;
    17. cout << func(1, 3) << endl; //正常函数的使用
    18. }
  • 相关阅读:
    curl 工具的使用
    Zookeeper和Nacos的区别
    【Python数据科学快速入门系列 | 06】Matplotlib数据可视化基础入门(一)
    代码规范的意义 为什么要规范代码 大厂程序员告诉你
    可以一键生成热点营销视频的工具,建议收藏
    gitlab环境准备
    如何实现罗克韦尔PLC AB1756的远程监控数据采集?
    gulimall基础篇回顾Day-10
    【强化学习】深度Q网络(DQN)求解倒立摆问题 + Python代码实战
    小程序初始创建
  • 原文地址:https://blog.csdn.net/m0_60416282/article/details/125607057