正在加载……
专注、离线、切勿分心
使用序列式容器:
     容器是STL的基础,容器分2种:
             ——序列式容器(sequential container)
             ——关联式容器(associative container)
     序列式容器会强调元素的次序,依次维护第一个元素、第二个元素……,直到最后一个元素,面向序列式容器的操作主要是迭代操作。




序列式容器的创建和元素的访问:
使用序列式容器,须包含相关的头文件,vector、list及deque分别对应:
#include  <vector>
#include  <list>
#include  <deque>
创建序列式容器的对象,大体有5种方式:

(1)创建空的容器,此时容器中的元素个数为0。
vector<int> obV;
list<float> obL;
deque<double> obD;

(2)vector<double> obV(10);  //vector型对产生特定大小的容器,此时容器中的元素被创建,编译器使用默认值为元素隐式初始化,像int、float和double等内建的数据类型会被初始化为0,对于类对象元素,将调用其无参构造函数(用户定义的或编译器缺省提供的)或每个参数都有默认值的构造函数。象obV中含10个double型元素, 初始化为0
list<int> obL(20);         //list型对象obL中含20个int型元素, 初始化为0
deque<float> obD(30);       //deque型对象obD中含30个float型元素, 初始化为0

(3)在(2)的基础上更进一步,创建特定大小的容器,并且为其中的每个元素指定初始值,此时在元素多少的参数后增加一个参数。
vector<int> obV(10,8);          //10个int型元素, 每个都初始化为8
list<double> obL(20,3.1);           //20个double型元素, 每个都初始化为3.1
deque<string> obD(30,"Hello");       //30个string型元素, 每个都初始化为"Hello"

(4)根据已有同类型的容器创建新容器,并将其中的元素完全复制过来,设obV1、obL1和obD1都是现成的容器,里面存储的数据均为int型,则可用下述命令创建新容器
vector<int> obV2(obV1);    //或vector<int> obV2=obV1;
list<int> obL2(obL1);      //或list<int> obL2=obL1;
deque<int> obD2(obD1);     //或deque<int> obD2=obD1;

(5)通过一对迭代器(可暂时理解为指针),以使编译器决定元素的个数和初值,这对迭代器用以标识一组元素区间。
int sz[5]={11,2,3,4,5};
vector<int> obV(sz, sz+5);
list<int> obL(sz, sz+5);
deque<int> obD(sz, sz+5);

vector和deque类的容器创建后就可以通过容器名[下标]或容器名.at(序号)的形式对元素进行随机访问(这是因为这2种类模板对下标运算符[]进行了重载);也支持迭代器访问。
但list类的容器不支持下标运算符[],无法使用[]对元素进行随机访问。但支持双向迭代器访问,如:
        list<int>::iterator iter = obL.begin();

#include <iostream>
#include<deque>
using namespace std;
int main()
{
        double arr[5]={1,2,3,4,5};
        deque<double> deque1(arr,arr+5);           //迭代器的区间[ )
        //随机访问迭代器
        for(int idx=0;idx!=deque1.size();++idx)
        {
                cout << deque1[idx]<<"  ";
        }
        cout<<endl;
        //双向迭代器,双向队列,两边都可以输入输出
        deque<double>::iterator it = deque1.begin();
        for(;it!=deque1.end();++it)
        {
                cout<<*it<<"  ";
        }
        cout<<endl;
        it = deque1.end();
        --it;
        for(;it!=deque1.begin()-1;--it)
        {
                cout<<*it<<"  ";
        }
        cout<<endl;
        //c++11 新特性编译要加上 -std=c++11
        for(auto & elem : deque1)
                cout<<elem<<"  ";
        cout<<endl;
        return 0;
}
#include <iostream>
#include <list>
using namespace std;
int main()
{
        list<float> list1(3,5);
        //list  没有重载下标运算符,所以list不支持随机访问
        list<float>::iterator it;
        for(it = list1.begin();it!=list1.end();++it)
        {
                (*it) += 2;
                cout<< *it <<"  ";
        }
        cout<<endl;
        list<float> list2(4,9);
        //进行交换,并没有移动数据,只是交换了指针而已
        list1.swap(list2);
        for(it = list1.begin();it != list1.end();++it)
        {
                (*it) += 2;
                cout<< *it <<"  ";
        }
        cout<<endl;
        return 0;
}

                  



序列式容器中元素的插入和删除:
在创建普通数组时,需要指定元素的个数,元素的插入和删除很繁琐。但在序列式容器中,只要调用操作函数,所有的事情都由STL类库自动完成,而且容器对象都能随着元素的插入和删除自动地增大或缩小
下面介绍5类相关函数:
(1)在容器尾部进行插入和删除(list、deque和vector都适用):
        void push_back(t)和void pop_back(void)
(2)在容器头部进行插入和删除(list和deque适用,vector不适用):
        void push_front(t)和void pop_front(void)
(3)获取容器头部和尾部元素(list、deque和vector都适用):
        front(void)和back(void)
(4)在容器中间插入元素。有如下3种重载形式:
          1、将元素t插到p之前,返回的迭代器指向被插入的元素。
              iterator insert(iterator p, elemType t);
          2、在p之前插入n个t,无返回值。
              void insert(iterator p, int n, elemType t);
          3、在p之前插入[first, last)之间的所有元素。插入的多是一个数组
              void insert(iterator p, iterator first, iterator last);
(5)erase删除操作,有2种重载形式:
          1、删除迭代器p所指向的元素,返回p指向的下一个迭代器:
              iterator erase(iterator p);
          2、删除[frist,last)之间的所有元素,返回last指向的下一个迭代器:
              iterator erase(iterator first, iterator last);
(6)clear操作:用于将容器对象清空
         void clear(void);    //但是容器capacity不会变,size变成0
         对于std::list还有一种删除方法: 成员函数 remove/remove_if
#include <iostream>
#include <vector>
using namespace std;
template <typename Container>
void display (Container & c)
{
        //typename关键字告诉编译器iterator是一个类类型,而不是其他的
        typename Container::iterator it;    //VS不加也可以
        for(it = c.begin(); it != c.end(); ++it)
        {
                cout<<*it<<"  ";
        }
        cout<<endl;
}
int main()
{
        vector<int> vec(5,0);
        display(vec);

        vector<int>::iterator it = vec.end();   //这里注意,vec的范围[ ),所以这里it指向的是最后一个元素的后面
        it=vec.insert(it,1);
        display(vec);
        cout<<"*it="<<*it<<endl;

        vec.insert(it,2,3);
        display(vec);

        it=vec.begin();
        int arr[3]={1,2,3};
        vec.insert(it,arr,arr+3);
        display(vec);
       // cout<<*it<<endl;    //这里不能输出,因为前面的insert没有返回指针,这里泄露了
        cout<<"------------删除开头元素------------"<<endl;
        vec.erase(it=vec.begin());
        for(auto & elem:vec)
                cout<<elem<<"  ";
        cout<<endl;
        cout<<"------------------------------------"<<endl;
        //c++新特性,编译加上   -std=c++11
        vector<int> vec2 = {1,2,3,4,5,6,7,8,9};
        display(vec2);
        return 0;
}

#include<iostream>
#include<vector>
using namespace std;
template<class container>
void print(container& v)
{
        cout<<"capacity="<<v.capacity()<<"  size="<<v.size()<<endl;
}
int main()
{
        vector<int> ve = {1,2,3};  // C++11新特性
        print(ve);
        ve.resize(10); // 更改空间大小10,没有数值的空间赋0,这时候size和capacity都是10
        print(ve);
        for(auto& elem:ve)
                cout<<elem<<"  ";
        cout<<endl;
        ve.resize(100);
        print(ve);
        ve.clear();  //清空有效元素,size变0
        print(ve);
        ve.push_back(4);
        ve.push_back(5);
        ve.push_back(6);
        print(ve);
        ve.resize(2);
        print(ve);
        for(auto& elem:ve)
                 cout<<elem<<"  ";
        cout<<endl;
        //vector<int>(ve).swap(ve);  // 用ve里面的有效数值初始化一个临时vector,swap交换指针,这时候ve的指针就交换了指向临时变量的空间,达到了删除没有存放数值的空间
        //删除没有存放数据的单元,释放空间
        //表示创建一个临时的vector<int>(vec) 这个临时的vec会去拷贝vec中的元素,然后再交换,这样vec的大小就变成3了,执行完后释放临时的vec
        ve.shrink_to_fit();  // 等价上面
        print(ve);
        cout<<"-------------"<<endl;
       
        vector<int> ve2;
        print(ve2);
        ve2.reserve(10);  // 开辟空间,区别resize更改空间大小
        print(ve2);
        ve2.push_back(1);
        print(ve2);
        return 0;
}
//reserve只是开辟空间,resize更改空间大小,改大了,多出空间全部初始化0,改小了就从尾删除多出的空间,元素也相应删除
//reserve改的是capacity,resize改的是size



vector容器:
      vector是数组的类表示,它提供了自动管理内存的功能,可以动态改变vector对象的长度,并随着元素的增删而增大或缩小,提供了对元素的随机访问,和数组一样,在vector尾部添加和删除元素(push_back和pop_back)的时间是固定的,但在vector中间或头部增删元素(insert,erase)的时间和复杂度线性正比于vector容器对象中元素的多少。


deque容器:
      deque表示双端队列(double-ended queue),deque容器对象支持下标随机访问,在deque头部和尾部添加删除元素时的时间都是固定的,因此,如果有很多操作是针对序列的头部位置的,建议使用deque容器。但是,如果是在deque的中间进行元素的增删处理,操作的复杂度和时间正比于deque对象中元素的多少。


list容器:
    list类模板表示双向链表,除了首尾元素外,list容器对象中的每个元素都和前面的元素相链接。list不支持下标随机访问,只能通过迭代器双向遍历。和vector和deque不同的是,在list的任何位置增删元素的时间都是固定的。
#include<iostream>
#include<list>
using namespace std;
bool removeIf(int n)
{
        return n>10;
}
int main()
{
        list<int> l1 = { 1,100,2,3,10,1,11,-1,12 };
        for(auto elem:l1)
                cout<<elem<<"  ";
        cout<<endl;
        l1.remove(1);  // 删除所有1
        for(auto elem:l1)
                cout<<elem<<"  ";
        cout<<endl;
        l1.remove_if([](int n){return n>10;});  // lambda表达式
        //l1.remove_if(removeIf);  //和上面一样的效果,删除所有大于10的元素
        for(auto elem:l1)
                cout<<elem<<"  ";
        cout<<endl;
        return 0;
}

posted on 2018-05-07 09:31  正在加载……  阅读(269)  评论(0编辑  收藏  举报