【C++学习】9.顺序容器 array,vector,string,deque,list,forward_list详解

元素在顺序容器中的顺序与其加入容器时的位置相对应。
关联容器中的元素位置由元素相关联的关键字值决定

所有容器类都共享公共的接口,不同的容器按照不同的方式对其进行拓展。
  • 一个容器就是一些特定类型对象的集合。顺序容器为程序员提供了控制元素存储访问顺序的能力。
  • 访问顺序不依赖于元素的值,而是与元素加入容器时的位置相对性。
  • 标准库提供了三种容器适配器,分别为容器操作定义了不同的接口,来与容器类型适配

1.1 顺序容器概览

所有的顺序容器都提供了快速访问元素的能力,但是这些容器在以下方面都有不同的性能折中:

  • 向容器中添加或者从容器中删除元素的代价
  • 非顺序访问元素的代价
顺序容器类型
array 固定大小数组 支持快速随机访问 不能添加或者删除
vector 可变大小数组 支持快速随机访问 尾部之外的位置插入或者删除元素可能很慢
string 与vector相似的容器,但是专门用来保存字符 支持快速随机访问 尾部插入和删除速度很快
deque 双端队列 支持快速随机访问 头尾位置插入和删除速度很快
list 双向链表 支持双向顺序访问 在list中任何位置进行插入/删除操作速度都很快
forward_list 单向链表 只支持单向顺序访问 在链表中任何位置进行插入/删除操作速度都很快
  • 除了固定大小的array外,其他容器都提供高效灵活的内存管理,我们可以添加和删除元素
  • string和vector将元素保存在连续的内存空间中,根据元素下标来计算地址是非常快速的,但是在这两种元素中间位置添加或者删除元素就会非常耗时
  • list和forward_list两个容器的设计目的是另容器的中间位置添加或者删除元素都很快,最为代价这两个容器不支持随机访问
  • deque支持快速随机访问,与string和vector一样,在deque的中间位置添加或者删除元素的代价很高,在两端添加和删除元素很快。
  • forward_list和array是C++新增的类型,与内置数组相比,array是一种更安全,更容易使用的数组类型。
  • 在这里插入图片描述

1.2 容器库概览

  • 有些操作是所有容器类型都提供的
  • 另外一些只支持顺序容器或者关联容器或者无序容器
  • 还有一些操作只适用于一小部分容器

一般来说,每个容器都定义在一个与类型名相同的头文件中

  1. queue定义在queue
  2. list定义在list
  3. 以此类推

顺序元素对保存的元素类型的限制

  • 顺序元素几乎可以保存任意类型的元素,但是某些容器操作对元素类型有其特殊要求。
  • 可以为不支持特定操作的类型定义容器,这种情况下只能使用那些没有特殊要求的容器操作。
    在这里插入图片描述
容器操作
类型别名
iterator 此容器类型 迭代器类型
const_iterator 可以读取元素,但是不能修改元素 迭代器类型
size_type 无符号整数类型 足以保存此种容器类型最大可能容器的大小
difference_type 带符号整数类型 足以保存两个迭代器之间的距离
value_type 元素类型
reference 元素的左值类型,与value_type&含义相同
const_reference 元素的const左值类型,与const value_type&含义相同
构造函数
C c; 默认构造函数
C c1(c2); 构造c2的拷贝c1
C(b,e) 构造c,将迭代器b和e制定的范围内的元素拷贝到c
C c{a,b,c…} 列表初始化c
赋值与swap
c1=c2 将c1中的元素替换为c2中的元素
c1={a,b,c…} 将c1中的元素替换为列表中的元素(不适用于array)
a.swap(b) 交换a与b中的元素
swap(a,b) 与a.swap(b)等价
大小
c.size() c中元素的数目(不支持forword_list)
c.max_size() c可保存的最大元素数目
c.empty() 若c中存储了元素,返回false
添加删除元素 在不同的容器中中这些接口都不同
c.insert(args) 将args中的元素拷贝进c
c.emplace(inits) 使用init构造c中的一个元素
c.erase(args) 删除args指定的元素
c.clear(); 删除c中所有的元素,返回void
关系运算符
== != 所有容器都支持
<,<=,>,>= 关系运算符(无序容器不支持)
获取迭代器
c.begin(),c.end() 首位迭代器
c.cbegin(),c.cend() 返回const_iterator
反向容器的额外成员 不支持forward_list
reverse_iterator 按逆序寻址元素迭代器
const_reverse_iterator 不能修改元素的逆序迭代器

迭代器

迭代器

  • 一个迭代器范围由一对迭代器表示。
  • 元素范围为左闭右开[begin,end)

容器类型成员

在这里插入图片描述

begin()和end()成员

//显式制定类型
list<string>::iterator it5=a.begin();
list<string>::const_iterator it6=a.begin();
//当且仅当a是const时,it7是const_iterator
auto it7=a.begin();
auto it8=a.cbegin();//是const_iterator

容器的定义和初始化

构造函数
C c; 默认构造函数
C c1(c2); 构造c2的拷贝c1
C(b,e) 构造c,将迭代器b和e制定的范围内的元素拷贝到c
C c{a,b,c…} 列表初始化c
赋值与swap
c1=c2 将c1中的元素替换为c2中的元素
c1={a,b,c…} 将c1中的元素替换为列表中的元素(不适用于array)
a.swap(b) 交换a与b中的元素
swap(a,b) 与a.swap(b)等价

初始化

将一个容器初始化为另一个容器的拷贝

  1. 直接拷贝整个容器C c1(c2) c1=c2。要求两个容器类型及其元素类型必须匹配。
  2. 拷贝由一个迭代器对指定的元素范围C(b,e),不要求容器类型和元素类型是相同的,只要将要拷贝的元素转换为要初始化的容器的元素类型即可。
  3. 在这里插入图片描述

列表初始化

在这里插入图片描述

与顺序容器大小相关的构造函数

除了与关联容器相同的构造函数外,顺序容器(array除外)还提供宁外一种构造函数。接受一个容器大小和一个(可选的)元素初始值,如果不提供初始值,则标准库会创建一个值初始化器。

vector<int> ivec(10,-1);
list<string> svec(10,"hi!");
forward_list<int> ivec(10);//0
deque<string> svec(10);//空sting

没有默认构造参数的类型必须提供初始值。
只有顺序容器洁柔大小参数,关联容器不支持

标准库具有固定大小

当定义一个array时,除了指定数组元素,还要指定容器大小。
内置数组类型不能进行拷贝或者对象赋值操作,array可以。
要求要求两个容器类型及其元素类型必须匹配,而且大小也得一样。

array<int,10> ial;
array<int,10> ia2={
    
    0,1,2,3,4};

3.顺序容器的操作

上一节说了顺序容器和关联容器都支持的操作,下面介绍支持顺序容器所特有的操作。

3.1 向顺序容器中添加元素

除了array,其他的顺序容器都可以动态添加或者删除元素。
下表array统统不适用
在这里插入图片描述

push_back

  • 所有顺序容器vector,string,list,forward_list,deque包括string都可以push_back

push_front

  • list,forward_list,deque,支持push_front,vector,string不行

指定位置插入insert

  • vector,list,string,deque,都支持insert,forward_list支持特殊版本的insert。
  • insert第一个参数是迭代器,指向插入元素位置的后一个元素
  • 可以把insert用在没有push_front操作的容器中,比如vector,string,但是除了末尾位置都会很慢
svec.insert(svec.begin(),"hello");
  • 把insert用在vector,string,deque,中间任何位置都可以,但是会很好事
insert 的几个版本

1.svec.insert(svec.end(),10,"anna");
2. svec.insert(svec.end(),v.end()-2,v.end());
3. svec.insert(svec.end(),{1,3,4,5});
第二种方法迭代器不能指向本身

使用insert的返回值可以在一个位置反复插入元素

list<string> lst;
auto iter=lst.begin();
while(cin>>word)
iter=lst.insert(iter,word);

使用emplace

  1. 新标准出了emplace_front,emplace和emplace_back
  2. 使用emplace_back时,将参数传递给元素类型的构造函数,会在容器管理的内存空间中直接创建对象,而调用push_back则会创建一个临时变量,并将其压入容器中。

3.2 访问元素

在这里插入图片描述

  • 所有容器都有一个front函数
  • 除了forward_list都有back函数
  • 用这两个一定得保证容器不为空
if!c.empty(){
    
    
auto val=*c.begin(),val2=c.front();
auto vals=*(--c.end());
auto val4=c.back();
}
  • 访问(at,下标,front,back)返回的都是引用,如果是const对象返回的是const引用,如果不是conts,则返回的是普通引用,可以改变元素值;
while(!c.empty()){
    
    
c.front()=42;//改变值
auto &v=c.back();
v=43;
auto v2=c.back();
v=0;//未改变不是一个引用}

3.3删除元素

下表除array
在这里插入图片描述

3.5 改变容器大小

list<int> ilist(10,42);
ilist.resize(15);//+五个0
ilist.resize(25,-1);//加10个-1
ilist.resize(5);//删除20个元素

容器操作可能使迭代器失效

当向容器中添加删除元素的操作可能使指向容器元素的指针,引用迭代或者器失效

不要保存end返回的迭代器

必须反复调用end,不能在循环之前保存end返回的迭代器

vector<char> s={
    
    'a','b','c','d'};
    auto b=s.begin();
    auto e=s.end();
    e--;

    while(b!=e){
    
    
        char tmp=*b;
        *b=*e;
        *e=tmp;
        b++;
        e--;
    }

4.vector 对象是如何增长的

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_37581730/article/details/108440346