第十章:序列与关联容器(一)

容器概述

容器是一种特殊的类型,其对象可以放置其他类型的对象(元素)。通常来讲,容器需要支持以下的操作:

  • 对象的添加
  • 对象的删除
  • 对象的索引
  • 对象的遍历

以上所述只是通常的情况,有一些容器只支持其中一部分操作

有多种算法可以用来实现容器,每种方法各有利弊。C++中的容器可以大致分为以下四类:

  • 序列容器:其中的对象有序排列,使用整数值进行索引
  • 关联容器:其中对象的顺序并不重要,使用键来进行索引
  • 适配器:调整原有容器的行为,使得其对外展现出新的类型、接口或者返回新的元素
  • 生成器:构造元素序列

关于容器,我们必须提到一个非常重要的概念:迭代器。迭代器用于指定容器中的一段区间,以执行遍历、删除等操作。我们可以使用以下方法来获取迭代器:

  • (c)begin/(c)end
    std::vector<int>x{1,2,3};
    auto b = x.begin();
    auto e = x.end();
    for(auto ptr = b;ptr != e;++ptr){
        std::cout << *ptr << " ";
    }
    
  • (c)rbegin/(c)rend:逆序迭代器
    std::vector<int>x{1,2,3};
    auto b = x.rbegin();
    auto e = x.rend();
    for(auto ptr = b;ptr != e;++ptr){
        std::cout << *ptr << " ";
    }
    

每种容器对应的迭代器可以支持的操作并不相同,大体来说,迭代器可以分成以下五类
在这里插入图片描述
关于每种迭代器的具体描述请参考这里。当然有些容器并不提供迭代器,在C++20中,我们将提供迭代器的容器称为Range

序列容器

C++标准库中提供了多种序列容器模版,包括

  • array:元素个数固定的序列容器,不支持对象的添加和删除
  • vector:元素在内存中连续储存的序列容器
  • forward_list/list:基于链表/双向链表的容器
  • deque:vector和list的折中,将整个容器分成若干了段,段内元素连续存储,段之间使用类似链表的结构来进行连接
  • basic_string:提供了对字符串专门的支持

在使用时,我们需要使用元素类型来实例化容器模版,从而构造可以保存具体类型的容器。不同容器所提供的接口大致是相同的,但根据容器性质的差异,其内部实现与复杂度不同。如果某个接口对于某些特定的容器的实现复杂度非常高,这些容器会提供相对较难使用的接口或者根本不提供相应的接口。

array容器模版

array是一个具有固定长度的容器,其内部维护了一个内建数组,与内建数组相比提供了复制操作。array主要提供了以下接口:

  • 构造:与内建数组相似
    std::array<int ,3> x = {1,2,3};
    
  • 获取元素类型
    std::array<int ,3>::value_type y
    
  • 元素访问:[]atfrontbackdata
    x[1];
    x.at(1);					// 越界会直接崩溃
    x.front();
    x.back();
    x.data();					// 返回指针,指向x中内建数组的第一个元素
    
  • 容量相关(平凡实现):emptysizemax_size

    注意empty,sizemax_size的返回值在编译期就已经确定了。如果std::array<type,size_t>中的size_t不为0,empty返回false,否则返回truesizemax_size的返回值与size_t的值相同。

  • 填充array内的元素:fill
  • 将两个array内的元素进行交换:swap

    注意,array的复制操作和swap操作的实现复杂度非常高

  • 比较操作:==<=>(C++20),具体的参考这里

    注意,参与比较的两个array的类型要完全相同,包括元素类型和元素个数

  • 迭代器:c(begin)/(c)end/(c)rbegin/(c)rend

接下来的剩余的容器模版只会着重介绍与array不同的地方,对于相同的部分请自行参考cppreference

vector容器模版

与array不同,vector中元素的数目是可以改变的,vector内部的实现如下图所示
在这里插入图片描述

其中buffer中浅灰色代表已经使用的内存,深灰色代表已分配但未使用的内存

与array一样,vector也提供了许多接口,其中大部分与array类似,但也有一些特殊的接口

  • 容量相关接口:`
    1. capacity:返回buffer的长度,注意这里不是vector内已有元素的个数
    2. reserve:一次性开辟固定长度的buffer,避免在添加元素时不断拓充buffer带来的性能下降
    3. shrink_to_fit:对buffer进行瘦身,开辟一块与现有元素个数大小相同的buffer并将所有的元素拷贝到新的buffer中,避免占用过多的内存
  • 附加元素接口
    1. push_back:在vector的结尾添加元素
    2. empllace_back:与push_back类似,但是是在内存中原地构造,不需要先构造一个临时变量再将临时变量push_back进去
  • 元素插入接口
    1. insert:在vector中间插入元素
    2. emplace:与insert类似,但是是在内存中原地构造

      insertemplace需要使用迭代器进行插入

  • 元素删除接口
    1. pop_back:删除最后一个元素
    2. erase:删除vector中任意一个元素

      erase需要使用迭代器进行删除

    3. clear:删除所有元素

在使用这些接口时,需要注意以下几点

  • vector不提供push_front/pop_front的接口,可以使用insert/erase来模拟,但是效率不高
  • vector的swap操作效率很高,只需要交换buffer的指针和对应的sizecap的值
  • vector的写操作可能会使迭代器失效,主要的原因就是buffer的改变,具体的参考Iterator invalidation

list容器模版

list是使用双向链表实现的序列容器,双向链表的结构如下图所示
在这里插入图片描述
与vector相比,list具有以下特点

  • 插入和删除的成本较低,但随机访问的成本较高
  • list提供了pop_frontpush_front接口
  • list提供了splice接口:将一个list容器中的内容移动到另外一个list容器中,本质上只修改了指针的值,效率非常高
  • list的写操作通常不会使迭代器失效

forward_list

使用单向链表实现的序列容器,单向链表的结构如下图所示
在这里插入图片描述
相比list,forward_list提供了成本较低的序列容器的实现,forward_list相比list具有以下特点

  • forward_list的迭代器只支持递增操作,因此没有(c)rbegin/(c)rend等接口
  • forward_list不支持使用size函数获取元素个数
  • forward_list不支持push_back/pop_back操作
  • forward_list支持许多XXX_after操作,具体参考这里

deque容器模版

deque是vector与list的折中,deque中元素的典型布局如下图所示
在这里插入图片描述
一种可能的deque实现如下图所示
在这里插入图片描述
deque具有以下特点

  • 与vector相比,deque的push_backpush_front速度较快
  • 与list相比,deque支持随机访问,但性能不如vector
  • deque在序列中间插入、删除元素的速度较慢

一般来说,我们很少会使用到deque,只有当我们希望使用一个可以支持类似vector的操作,同时push_front的速度较快的容器时才会考虑deque

basic_string容器模版

basic_string与vector非常类似,但实现了字符串相关的接口,具体的请参考这里。关于basic_string,我们需要注意以下几点

  • basic_string使用char实例化出了std::string
  • basic_string提供了如findsubstr等字符串特有的接口
  • basic_string提供了数值与字符串转换的接口,比如to_stringstoistod
  • basic_string针对短字符串进行了优化,具体的请参考SSO
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值