🍆容器库共同操作
2022-5-20
| 2023-8-2
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password
Property

 
通常每个容器定义在与其名字相同的头文件中。deque定义在 <deque> 头文件中,list定义在 <list> 头文件中。容器都是类模板。与 vector一样,使用时必须提供额外的信息来生成特定的容器类型。对于绝大部分容器来说,还需提供的是元素的类型: 
 
容器类型上的操作形成了一种层次:
  • 某些操作是所有容器类型都提供的
  • 一些操作仅针对顺序容器、关联容器或无序容器
  • 还有一些操作只适用于一小部分容器
 
以下是所有容器共同的操作:
notion image
notion image
 
容器可以容纳的元素类型
顺序容器几乎可以保存任意类型的元素,甚至可以定义容器的元素类型是另外一个容器: 
虽然可以在容器中保存几乎任何类型,但某些容器操作对元素类型有其自己的特殊要求。我们可以为不支持特定操作需求的类型定义容器,但这种情况下就只能使用那些没有特殊要求的容器操作了。
 
例如,顺序容器构造函数的一个版本接受容器大小参数,它使用了元素类型的默认构造函数。但某些类没有默认构造函数。我们可以定义一个保存这种类型对象的容器,但在构造这种容器时不能只传递给它一个元素数目参数:
 

迭代器

与容器一样,迭代器有着公共的接口,如果一个迭代器提供某个操作,那么所有提供相同操作的迭代器对这个操作的实现方式都是相同的。下表列出了所有容器都支持的操作:
notion image
forward_list 容器的迭代器不支持自减操作符,只有 stringvectordequearray 支持迭代器的算术运算。
 
迭代器范围
一个迭代器范围由一对迭代器表示。这两个迭代器通常被称为beginend,分别指向同一个容器中的元素或尾后地址。end迭代器不会指向范围中的最后一个元素,而是指向尾元素之后的位置。这种元素范围被称为 左闭合区间,其标准数学描述为 [begin,end)。迭代器beginend必须指向相同的容器,end 可以与begin指向相同的位置,但不能指向begin之前的位置。
 
如果满足如下条件,两个迭代器beginend构成一个迭代器范围:
  • 它们指向同一个容器中的元素,或者是容器最后一个元素之后的位置
  • 可以通过反复递增begin来到达end
 
 
假定beginend构成一个合法的迭代器范围,则:
  • begin等于end,则范围为空
  • begin不等于end,则范围内至少包含一个元素,且begin指向该范围内的第一个元素
  • 可以递增begin 若干次,令begin 等于end
 
 

容器类型成员

每个容器都定义了多个类型。如:size_type ,iterator 和 const_iterator 。绝大多数的容器还提供反向迭代器,反向迭代器的 ++ 将使得迭代器指向前一个元素,从而达到反向遍历容器元素的目的。
类型别名使得我们使用容器的元素类型时不需要真正知道其类型是什么。如果需要元素类型,只要使用 value_type 就可以,如果需要值的引用类型,就使用 reference 或者 const_reference 就可以。这些元素相关的类型别名在泛型编程中十分有用处。
 
使用这些类型需要用容器的类型加以限定:
这些声明语句使用了作用域运算符来说明希望使用list<string>类的iterator成员及vector<int>类定义的difference_type
 

begin和end成员

beginend 操作生成指向容器中第一个元素和尾后地址的迭代器。其常见用途是形成一个包含容器中所有元素的迭代器范围。
beginend 操作有多个版本:带r的版本返回反向迭代器。以c开头的版本(C++11新增)返回 const 迭代器(const_iterator)。不以c开头的版本都是重载的,当对非常量对象调用这些成员时,返回普通迭代器(iterator),对const对象调用,返回 const_iterator
 
c 开头的版本是C++新标准引入的,用以支持 autobeginend 函数结合使用。
autobeginend 结合使用时,返回的迭代器类型依赖于容器类型。但调用以c开头的版本仍然可以获得const_iterator,与容器是否是常量无关。
当程序不需要访问时,应该使用 cbegincend
 
 

容器定义和初始化

每个容器类型都定义了一个默认构造函数。除array之外, 其他容器的默认构造函数都会创建一个指定类型的空容器, 且都可以接受指定容器大小和元素初始值的参数。
notion image
 

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

将一个新容器创建为另一个容器的拷贝的方法有两种:
  1. 可以直接拷贝整个容器
  1. (array 除外)拷贝由一个迭代器对指定的元素范围
将一个容器初始化为另一个容器的拷贝时,两个容器的容器类型和元素类型都必须相同。不过,当传递迭代器参数来拷贝一个范围时,就不要求容器类型相同,而且新容器和原容器中的元素类型也可以不同,但是要能进行类型转换。
 
接收两个迭代器的构造函数使用它们来表示想要拷贝的元素范围。其中一个表示首元素另一个表示尾后元素。新的容器的大小与范围中的大小一致。新容器中的每个元素都是用范围内元素进行初始化的。
由于迭代器表示一个范围,可以使用构造函数拷贝一个容器的子序列:
 

列表初始化

C++11允许对容器进行列表初始化
列表初始化会指定容器中的每个元素的值。除了 array 之外,初始化列表同时暗含了容器的大小:容器大小与元素的初始化列表中的个数一样多。
 

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

上面所介绍的所有构造函数同时适用于关联容器。除此之外顺序容器(除了array外)还可以指定大小和可选的元素初始值。如果没有提供元素初始值,那么将创建一个值初始化的容器:
如果元素类型是内置类型或者是具有默认构造函数的类类型,可以只为构造函数提供一个容器大小参数。如果元素类型没有默认构造函数,除了大小参数外,还必须指定一个显式的元素初始值。
注:只有顺序容器的构造函数才接受大小参数,关联容器并不支持。
 

标准库array具有固定大小

定义和使用 array 类型时,需要同时指定元素类型和容器大小:
由于大小是array类型的一部分,array不支持普通的容器构造函数。这些构造函数都会确定容器的大小,要么隐式地,要么显式地。而允许用户向一个array构造函数传递大小参数,最好情况下也是多余的,而且容易出错。
 
array大小固定的特性也影响了它所定义的构造函数的行为。与其他容器不同,默认构造的array是非空的:它包含了与其大小一样多的元素。这些元素都被默认初始化,就像一个内置数组中的元素那样。
array 进行列表初始化时,初始值的数量不能大于 array 的大小。如果初始值的数量小于 array 的大小,则只初始化靠前的元素,剩余元素会被值初始化。如果元素类型是类类型,则该类需要一个默认构造函数,以使值初始化能够进行:
 
值得注意的是尽管不能拷贝或赋值内置数组,但是标准库array是可以拷贝和赋值的:
与其他容器一样,拷贝或赋值时类型必须是完全匹配的,对于array来说元素类型和尺寸都必须完全一样。
 
 

赋值和swap

notion image
与赋值相关的操作符都作用于整个容器。其将左边容器的全部元素替换为右边容器中的元素的副本:
第一个赋值运算后,左边容器将与右边容器相等。如果两个容器原来大小不同,赋值运算后两者的大小都与右边容器的原大小相同。第二个赋值运算后,c1的size变为3,即花括号列表中值的数目。
 
 
与内置数组不一样,标准库array类型支持赋值。左右边的操作数必须是相同的类型:
由于初始值列表中的元素个数可能与 array 中的元素个数不一样,array不支持用初始值列表进行赋值。基于同样的原因 array 同样不支持 assign 操作。
 
 

使用 assign(只有顺序容器支持)

赋值运算符两侧的运算对象必须类型相同。assign 允许用不同但相容的类型赋值,或者用容器的子序列赋值。
注意:由于旧元素被替换掉,传递给 assign 的迭代器一定不能指向调用 assign 的容器。
 
assign的第二个版本接收一个整数以及一个元素值。它将容器中的元素全部替换为特定数目的元素,每个元素值都与参数中给出的元素值一样:
 

使用 swap

swap 交换两个相同类型容器的内容。除 array 外,swap 不对任何元素进行拷贝、删除或插入操作,只交换两个容器的内部数据结构,因此可以保证快速完成。
对于 arrayswap 会真正交换它们的元素。因此在 swap 操作后,指针、引用和迭代器所绑定的元素不变,但元素值已经被交换。
对于其他容器类型(除 string),指针、引用和迭代器在 swap 操作后仍指向操作前的元素,但这些元素已经属于不同的容器了。
新标准库同时提供了成员和非成员函数版本的 swap。非成员版本的 swap 在泛型编程中非常重要,建议统一使用非成员版本的 swap
 
 

容器 size 操作

  • size 成员返回容器中元素的数量
  • emptysize 为0时返回 true,否则返回 false
  • max_size 返回一个大于或等于该类型容器所能容纳的最大元素数量的值
forward_list 支持 max_sizeempty,但不支持 size
 
 

关系运算符

每个容器类型都支持相等运算符(==!=)。除无序关联容器外,其他容器都支持关系运算符(>>=<<=)。关系运算符两侧的容器类型和保存元素类型都必须相同。
 
两个容器的比较实际上是元素的逐对比较,其工作方式与string的关系运算符类似:
  • 如果两个容器大小相同且所有元素对应相等,则这两个容器相等
  • 如果两个容器大小不同,但较小容器中的每个元素都等于较大容器中的对应元素,则较小容器小于较大容器
  • 如果两个容器都不是对方的前缀子序列,则两个容器的比较结果取决于第一个不等元素的比较结果
注意:只有元素类型同样定义了关系操作符,才能使用容器的关系操作符进行比较。
 
容器的相等运算符实际上是使用元素的 == 运算符实现的,而其他关系运算符则是使用元素的 < 运算符。如果元素类型不支持所需运算符,则保存该元素的容器就不能使用相应的关系运算。如:Sales_data不支持 ==和 <操作。那么就不能比较两个 Sales_data的容器元素:
  • C++
  • 顺序容器顺序容器操作
    目录