• C++中STL介绍


    转载自:https://blog.csdn.net/hhu1506010220/article/details/51971642

    介绍

    这篇文章的目的是为了介绍std::vector,如何恰当地使用它们的成员函数等操作。本文中还讨论了条件函数和函数指针在迭代算法中使用,如在remove_if()和for_each()中的使用。通过阅读这篇文章读者应该能够有效地使用vector容器,而且应该不会再去使用C类型的动态数组了。

     

    Vector总览

    vector是C++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库。vector之所以被认为是一个容器,是因为它能够像容器一样存放各种类型的对象,简单地说,vector是一个能够存放任意类型的动态数组,能够增加和压缩数据。

     

    Vector成员函数:

    函数 表述

    c.assign(begin,end)

    c.assign(n,element)

    截取c中[begin,end)区间的元素赋值给c

    截取c中n个element的拷贝赋值给c

    c.at(idx) 传回索idx所指的数据,如果idx越界,抛出out_of_range
    c.back() 传回最后一个数据,不检查这个数据是否存在
    c.begin() 传回迭代器中的第一个数据
    c.capacity() 返回容器中的数据个数
    c.clear() 移除容器中的所有数据
    c.empty() 判断容器是否为空
    c.end() 传回迭代器中的最后一个数据地址

    c.earse(pos)

    c.earse(begin,end)

    删除pos位置的数据,传回下一个数据的位置

    删除[begin,end)区间的数据,传回下一个数据的位置

    c.front() 传回第一个数据
    get_allocator 使用构造函数返回一个拷贝

    c.insert(pos,element)

    c.insert(pos,n,element)

    c.insert(pos,begin,end)

    在pos位置插入一个element拷贝,传回新数据位置

    在pos位置插入n个element数据,无返回值

    在pos位置插入【begin,end)区间的数据,无返回值

    c.max_size() 返回容器中最大数据的数量
    c.pop_back() 删除最后一个数据
    c.push_back(element) 在尾部加入一个数据
    c.rbegin() 传回一个逆向队列的第一个数据
    c.rend() 传回一个逆向队列的最后一个数据的下一个位置
    c.resize(num) 重新制定队列的长度

    c.reserve()

    c.size()

    c1.swap(c2)

    swap(c1,c2)

    vector<element> c

    vector<element>c1(c2)

    vector<element>c(n)

    vector<element>c(n,element)

    vector<element>c(begin,end)

    c.~vector<element>()

    保留适当的容量

    返回容器中实际数据的个数

    将c1和c2元素互换

    同上

    创建一个空的vector

    复制一个vector

    创建一个vector,含有n个元素,数据均已缺省构造产生

    创建一个含有n个element拷贝的vector

    创建一个以【begin,end)区间的vector

    销毁所有数据,释放内存

    创建一个Vector

    1     vector<string> strv(500);//创建包含500个string类型数据的vector
    2     vector<string> strv1(500,"0");//创建包含500个string类型数据的vector,并初始化为"0"
    3     vector<string> strv2(strv);

    向vector添加一个数据

    vector添加数据的缺省方法是push_back()。push_back()函数表示将数据添加到vector的尾部,并按需要来分配内存。

    1     vector<int> veri;
    2     for(int i=0;i<10;i++){
    3         veri.push_back(i);
    4     }

    获取vector中指定位置的数据

    很多时候我们不必要知道vector里面有多少数据,vector里面的数据是动态分配的,使用push_back()的一系列分配空间常常决定于文件或一些数据源。如果你想知道vector存放了多少数据,你可以使用empty()。获取vector的大小,可以使用size()。例如,如果你想获取一个vector v的大小,但不知道它是否为空,或者已经包含了数据,如果为空想设置为-1,你可以使用下面的代码实现:

    1 int nSize = v.empty() ? -1 : static_cast<int>(v.size());

    访问vector中的数据

    使用两种方法来访问vector。

    1、   vector::at()

    2、   vector::operator[]

    operator[]主要是为了与C语言进行兼容。它可以像C语言数组一样操作。但at()是我们的首选,因为at()进行了边界检查,如果访问超过了vector的范围,将抛出一个例外。由于operator[]容易造成一些错误,所有我们很少用它,下面进行验证一下:

     1     vector<int> v;
     2     v.reserve(10);
     3     for(int i=0; i<7; i++)
     4         v.push_back(i);
     5     try
     6     {
     7         int iVal1 = v[7];  // not bounds checked - will not throw
     8         int iVal2 = v.at(7); // bounds checked - will throw if out of range
     9     }
    10     catch(const exception& e)
    11     {
    12         cout << e.what();
    13     }

    我们使用reserve()分配了10个int型的空间,但并不没有初始化。  

    你可以在这个代码中尝试不同条件,观察它的结果,但是无论何时使用at(),都是正确的。

    删除vector中的数据
    vector能够非常容易地添加数据,也能很方便地取出数据,同样vector提供了erase(),pop_back(),clear()来删除数据,当你删除数据的时候,你应该知道要删除尾部的数据,或者是删除所有数据,还是个别的数据。在考虑删除等操作之前让我们静下来考虑一下在STL中的一些应用。
    压缩一个臃肿的vcetor

    很多时候大量的删除数据,或者通过使用reserve(),结果vector的空间远远大于实际需要的。所有需要压缩vector到它实际的大小。resize()能够增加vector的大小。Clear()仅仅能够改变缓存的大小,所有的这些对于vector释放内存等九非常重要了。如何来解决这些问题呢,让我们来操作一下。

    我们可以通过一个vector创建另一个vector。让我们看看这将发生什么。假定我们已经有一个vector veri,它的内存大小为1000,当我们调用size()的时候,它的大小仅为7。我们浪费了大量的内存。让我们在它的基础上创建一个vector。

    1     vector<int> veri;
    2     veri.reserve(1000);
    3     for(int i=0;i<10;i++){
    4         veri.push_back(i);
    5     }
    6     
    7     cout << "veri capacity="<<veri.capacity()<<endl;
    8     cout << "veri size="<<veri.size()<<endl;

    创建新的vector来与veri交换

     1     vector<int> veri;
     2     veri.reserve(1000);
     3     for(int i=0;i<10;i++){
     4         veri.push_back(i);
     5     }
     6     
     7     cout << "veri capacity="<<veri.capacity()<<endl;
     8     cout << "veri size="<<veri.size()<<endl;
     9     
    10     vector<int> veri1;
    11     veri1.swap(veri);
    12     
    13     cout << "veri capacity="<<veri.capacity()<<endl;
    14     cout << "veri size="<<veri.size()<<endl;
    15     cout << "veri1 capacity="<<veri1.capacity()<<endl;
    16     cout << "veri1 size="<<veri1.size()<<endl;

    新的vector的capacity变为1000,size为7。这样让没有达到目的

     1     vector<int> veri;
     2     veri.reserve(1000);
     3     for(int i=0;i<10;i++){
     4         veri.push_back(i);
     5     }
     6     
     7     cout << "veri capacity="<<veri.capacity()<<endl;
     8     cout << "veri size="<<veri.size()<<endl;
     9     
    10 //    vector<int> veri1;
    11 //    veri1.swap(veri);
    12     vector<int>(veri1).swap(veri1);
    13     cout << "veri capacity="<<veri.capacity()<<endl;
    14     cout << "veri size="<<veri.size()<<endl;
    15 //    cout << "veri1 capacity="<<veri1.capacity()<<endl;
    16 //    cout << "veri1 size="<<veri1.size()<<endl;

    达到目的!!!!

    同时,测试一下clear,可以发现clear只是把内容清楚了,但vector的内存并没有释放掉,可以使用swap来彻底释放内存。

     1     vector<int> veri;
     2     veri.reserve(1000);
     3     for(int i=0;i<10;i++){
     4         veri.push_back(i);
     5     }
     6     cout << "veri capacity="<<veri.capacity()<<endl;
     7     cout << "veri size="<<veri.size()<<endl;
     8 
     9     veri.clear();
    10     cout << "veri capacity="<<veri.capacity()<<endl;
    11     cout << "veri size="<<veri.size()<<endl;
    12     vector<int>(veri).swap(veri);
    13     cout << "veri capacity="<<veri.capacity()<<endl;
    14     cout << "veri size="<<veri.size()<<endl;

    迭代器

     1 // vector.cpp : 定义控制台应用程序的入口点。
     2 //
     3 
     4 #include "stdafx.h"
     5 #include <vector>
     6 #include <iostream>
     7 #include <numeric>
     8 #include <algorithm>
     9 using namespace std;
    10 
    11 bool Comp(const int &a,const int &b){
    12 
    13     return a > b;
    14 }
    15 int _tmain(int argc, _TCHAR* argv[])
    16 {
    17     vector<int> vec;
    18     //添加元素
    19     vec.push_back(10);
    20     vec.push_back(20);
    21     vec.push_back(30);
    22 
    23     vector<int>::iterator vecit ;
    24     cout << "初始:";
    25     for(vecit=vec.begin();vecit!=vec.end();vecit++){
    26         cout << *vecit << " ";
    27     }
    28     cout <<endl;
    29 
    30     cout << "求和="<<accumulate(vec.begin(),vec.end(),0)<<endl;
    31 
    32     //插入
    33     vec.insert(vec.begin(),1);
    34     vec.insert(vec.end(),40);
    35     cout << "插入:";
    36     for(vecit=vec.begin();vecit!=vec.end();vecit++){
    37         cout << *vecit << " ";
    38     }
    39     cout <<endl;
    40     //删除
    41     vec.erase(vec.begin());
    42     cout << "删除:";
    43     vec.erase(vec.begin(),vec.begin()+2);
    44     for(vecit=vec.begin();vecit!=vec.end();vecit++){
    45         cout << *vecit << " ";
    46     }
    47     cout <<endl;
    48     //转置
    49     reverse(vec.begin(),vec.end());
    50     cout << "转置:";
    51     for(vecit=vec.begin();vecit!=vec.end();vecit++){
    52         cout << *vecit << " ";
    53     }
    54     cout <<endl;
    55 
    56     vec.push_back(1);
    57     vec.push_back(3);
    58     vec.push_back(2);
    59     vec.push_back(55);
    60     vec.push_back(-1);
    61     vec.push_back(0);
    62     vec.push_back(2);
    63     vec.push_back(3);
    64     vec.push_back(4);
    65     cout << "初始:";
    66     for(vecit=vec.begin();vecit!=vec.end();vecit++){
    67         cout << *vecit << " ";
    68     }
    69     cout <<endl;
    70     sort(vec.begin(),vec.end());
    71     cout << "升序:";
    72     for(vecit=vec.begin();vecit!=vec.end();vecit++){
    73         cout << *vecit << " ";
    74     }
    75     cout <<endl;
    76     sort(vec.begin(),vec.end(),Comp);
    77     cout << "降序:";
    78     for(vecit=vec.begin();vecit!=vec.end();vecit++){
    79         cout << *vecit << " ";
    80     }
    81     cout <<endl;
    82     return 0;
    83 }

    字符串

    输入:

     1 #include "stdafx.h"
     2 #include <string>
     3 #include <iostream>
     4 
     5 using namespace  std;
     6 int _tmain(int argc, _TCHAR* argv[])
     7 {
     8     string s1;
     9     s1 = "hello";
    10     string s2;
    11     char s[1024];
    12     //scanf输入速度比cin快的多,但是scanf是c函数,不能输入字符串
    13     scanf("%s",s);
    14     s2 = s;
    15     cout << s1 << endl;
    16     cout << s2 << endl;
    17     return 0;
    18 }

    尾部添加字符字符串直接用+号 例如: s += 'a'; s += "abc",或者使用append方法,s.append(“123”)

    删除: 

     1 // string1.cpp : 定义控制台应用程序的入口点。
     2 //
     3 
     4 #include "stdafx.h"
     5 #include <string>
     6 #include <iostream>
     7 
     8 using namespace  std;
     9 int _tmain(int argc, _TCHAR* argv[])
    10 {
    11     string s;
    12     s = "0123456789";
    13 
    14     string::iterator it = s.begin();
    15     s.erase(it+3);//删除s[3]
    16     cout << s << endl;
    17 
    18     s = "0123456789";
    19     s.erase(it+1,it+4);//删除s[1]-s[3]
    20     cout << s << endl;
    21 
    22     s.clear();
    23     cout << s << endl;
    24 }

    查找(find)

      用find找到string里面第一个要找到元素(char或者串),找到返回数组下标,找不到返回end()迭代器

      string和vector有很多相同的东西,比如length(),size(),empty(),reverse(),相对也容易,就不一一说了。

    数字化处理(string)

      经常会遇到这样一种情况,有一个数字,需要把每一位给提取出来,如果用取余数的方法,花费的时间就会很长,所以可以当成字符串来处理,方便、省时。

      例子:求一个整数各位数的和

     1 // string1.cpp : 定义控制台应用程序的入口点。
     2 //
     3 
     4 #include "stdafx.h"
     5 #include <string>
     6 #include <iostream>
     7 
     8 using namespace  std;
     9 int _tmain(int argc, _TCHAR* argv[])
    10 {
    11     string s;
    12     s = "0123456789";
    13     int sum = 0;
    14     for(int i=0;i<s.size();i++){
    15         switch(s.at(i)){
    16         case '1':sum+=1;break;
    17         case '2':sum+=2;break;
    18         case '3':sum+=3;break;
    19         case '4':sum+=4;break;
    20         case '5':sum+=5;break;
    21         case '6':sum+=6;break;
    22         case '7':sum+=7;break;
    23         case '8':sum+=8;break;
    24         case '9':sum+=9;break;
    25         }
    26     }
    27     cout << "sum =" << sum << endl;
    28     
    29 }

    string与数值相互转换( sprintf <sstream> )

     1 // string1.cpp : 定义控制台应用程序的入口点。
     2 //
     3 
     4 #include "stdafx.h"
     5 #include <string>
     6 #include <iostream>
     7 #include <sstream>
     8 using namespace  std;
     9 
    10 string converToString(double x){
    11     ostringstream o;
    12     if(o << x){
    13         return o.str();
    14     }
    15     return "error";
    16 }
    17 
    18 double converFromString(string s){
    19     istringstream i(s);
    20     double x;
    21     if(i >> x){
    22         return x;
    23     }
    24     return 0.0;
    25 }
    26 int _tmain(int argc, _TCHAR* argv[])
    27 {
    28     char b[100];
    29     sprintf(b,"%d",1987);
    30     string s1 ;
    31     s1 = b;
    32     cout << s1 << endl;
    33 
    34     string s2 = converToString(1954);
    35     cout << s2 << endl;
    36 
    37     string s3 = "202";
    38     int c = converFromString(s3);
    39     cout << c << endl;
    40 
    41     string s4 = "casacsa6";
    42     int d = converFromString(s4);
    43     cout << d << endl;
    44 
    45     string s5 = "31af12";
    46     int f = converFromString(s5);
    47     cout << f << endl;
    48     return 0;
    49 }

    set容器

     set是用红黑树的平衡二叉索引树的数据结构来实现的,插入时,它会自动调节二叉树排列,把元素放到适合的位置,确保每个子树根节点的键值大于左子树所有的值、小于右子树所有的值,插入重复数据时会忽略。set迭代器采用中序遍历,检索效率高于vector、deque、list,并且会将元素按照升序的序列遍历。set容器中的数值,一经更改,set会根据新值旋转二叉树,以保证平衡,构建set就是为了快速检索(python中的set一旦建立就是一个常量,不能改的)。

      multiset,与set不同之处就是它允许有重复的键值。

    set的正反遍历:迭代器iterator,reverse_iterator

     1 // string1.cpp : 定义控制台应用程序的入口点。
     2 //
     3 
     4 #include "stdafx.h"
     5 #include <set>
     6 #include <iostream>
     7 
     8 using namespace  std;
     9 
    10 int _tmain(int argc, _TCHAR* argv[])
    11 {
    12     set<int> v;
    13     v.insert(1);
    14     v.insert(5);
    15     v.insert(7);
    16     v.insert(2);
    17     v.insert(4);
    18     v.insert(10);
    19 
    20     //中序遍历=升序遍历
    21     set<int>::iterator it;
    22     for(it=v.begin();it!=v.end();it++){
    23         cout << *it << " ";
    24     }
    25     cout << endl;
    26 
    27     set<int>::reverse_iterator it1;
    28     for(it1=v.rbegin();it1!=v.rend();it1++){
    29         cout << *it1 << " " ;
    30     }
    31     cout << endl;
    32     
    33     return 0;
    34 }

    自定义比较函数,insert的时候,set会使用默认的比较函数(升序),很多情况下需要自己编写比较函数。

      1、如果元素不是结构体,可以编写比较函数,下面这个例子是用降序排列的(和上例插入数据相同):

     1 // string1.cpp : 定义控制台应用程序的入口点。
     2 //
     3 
     4 #include "stdafx.h"
     5 #include <set>
     6 #include <iostream>
     7 
     8 using namespace  std;
     9 
    10 
    11 struct Comp
    12  {
    13     //重载()
    14      bool operator()(const int &a, const int &b)
    15      {
    16          return a > b;
    17      }
    18  };
    19 int _tmain(int argc, _TCHAR* argv[])
    20 {
    21     set<int> v;
    22     v.insert(1);
    23     v.insert(5);
    24     v.insert(7);
    25     v.insert(2);
    26     v.insert(4);
    27     v.insert(10);
    28 
    29     //中序遍历=升序遍历
    30     set<int>::iterator it;
    31     for(it=v.begin();it!=v.end();it++){
    32         cout << *it << " ";
    33     }
    34     cout << endl;
    35 
    36     set<int,Comp>::reverse_iterator it1;
    37     for(it1=v.rbegin();it1!=v.rend();it1++){
    38         cout << *it1 << " " ;
    39     }
    40     cout << endl;
    41     
    42     return 0;
    43 }

    map

      map也是使用红黑树,他是一个键值对(key:value映射),便利时依然默认按照key程序的方式遍历,同set。

    multimap

      multimap由于允许有重复的元素,所以元素插入、删除、查找都与map不同。

      插入insert(pair<a,b>(value1,value2))

    至于删除和查找,erase(key)会删除掉所有key的map,查找find(key)返回第一个key的迭代器

      deque

      deque和vector一样,采用线性表,与vector唯一不同的是,deque采用的分块的线性存储结构,每块大小一般为512字节,称为一个deque块,所有的deque块使用一个Map块进行管理,每个map数据项记录各个deque块的首地址,这样以来,deque块在头部和尾部都可已插入和删除元素,而不需要移动其它元素。使用push_back()方法在尾部插入元素,使用push_front()方法在首部插入元素,使用insert()方法在中间插入元素。一般来说,当考虑容器元素的内存分配策略和操作的性能时,deque相对vectore更有优势。

    (下面这个图,我感觉Map块就是一个list< map<deque名字,deque地址> >)

      插入删除

      遍历当然可以使用下标遍历,在这里使用迭代器。

      list

      list<int> l

      插入:push_back尾部,push_front头部,insert方法前往迭代器位置处插入元素,链表自动扩张,迭代器只能使用++--操作,不能用+n -n,因为元素不是物理相连的。

      遍历:iterator和reverse_iterator正反遍历

      删除:pop_front删除链表首元素;pop_back()删除链表尾部元素;erase(迭代器)删除迭代器位置的元素,注意只能使用++--到达想删除的位置;remove(key) 删除链表中所有key的元素,clear()清空链表。

      查找:it = find(l.begin(),l.end(),key)

      排序:l.sort()

      删除连续重复元素:l.unique() 【2 8 1 1 1 5 1】 --> 【 2 8 1 5 1】

      bitset

      从来没用过,上两幅图吧就:

      stack(后进先出)

      这个印象深刻,学数据结构的时候做表达式求值的就是用的栈。

  • 相关阅读:
    Linux中find常见用法示例
    PHP写的异步高并发服务器,基于libevent
    Prototype原型模式
    抽象工厂模式
    工厂方法模式
    简单工厂模式
    记一次皮肤过敏
    Thread类线程结束会唤醒使用其对象做锁而睡眠的线程
    位运算符
    逻辑运算符
  • 原文地址:https://www.cnblogs.com/zhangjxblog/p/8902010.html
Copyright © 2020-2023  润新知