C++标准库_顺序容器

 

C++标准库_顺序容器

标准库类型vector

       标准库类型vector表示对象的集合,其中所有对象的类型都相同。集合中的每个对象都有一个与之相对的索引,索引用于访问对象。

       想要使用vector,必须包含适当的头文件:

#include <vector>

using std::vector;

       vector是模板而非类型,由vector生成的类型必须包含/提示vector中元素的类型,例如vector<int>

       因为引用不是对象(只是对象的别名),所以不存在包含引用的vector。除此之外,其他绝大多数(非引用)内置类型和类类型都可以构成vector对象。

       在早期的C++标准中如果vector的元素还是vector(或者其它模板类型),则其定义的形式必须在外层vector对象的右尖括号和其元素类型之间添加一个空格,即应该写成vector<vector<int> >而非vector<vector<int>>

定义和初始化vector对象

       最常见的方式就是先定义一个空vector,然后当运行到获取到元素的值后再逐一添加。

vector<T> v1;                     //v1是一个空vector,它潜在的元素是T类型的,执行默认初始化

vector<T> v2(v1);        //v2中包含有v1所有元素的副本

vector<T> v2 = v1;      //等价于v2(v1)

列表初始化vector对象

vector<T> v5{a, b, c...};        //v5包含了初始值个数的元素,每个元素都被赋予相应的初始值

vector<T> v5 = {a, b, c...};    //等价于vector<T> v5{a, b, c...}

创建指定数量的元素

vector<T> v3(n, val);           //v3包含了n个重复的元素,每个元素的值都是val

值初始化

vector<T> v4(n);                 //v4包含了n个重复地执行了值初始化的对象

       如果vector对象的元素是内置类型,比如int,则元素初始值自动设为0.如果元素是某种类类型,比如string,则元素由类默认初始化

 

向vector中添加元素

       常见的处理方法是先创建一个空vector,然后在运行时再利用vector的成员函数push_back向其中添加元素。push_back负责把一个值当成vector对象的尾元素“压到(push)”vector对象的“尾端(back)”.

       注:如果循环体内部包含有向vector对象添加元素的语句,则不能使用范围for循环,因为范围for语句体内不应改变其所遍历序列的大小

 

其他vector操作

v.empty()              //如果v不含有任何元素,返回真;否则返回假

v. size()                 //返回v中元素的个数

       返回值的类型是由vector 定义的size_type 类型。要使用size_type,需首先指定它是由哪种类型定义的。vector对象的类型总是包含着元素的类型:

vector<int> : : size_type      // 正确

vector: : size_type               // 错误

 

v. push_back(t)      //向v的尾端添加一个值为t的元素

v[n]                      //返回v中第n个位置上的元素的引用

       下标的类型是相应的size_type类型。vector 对象(以及string 对象)的下标运算符可用于访问已存在的元素,而不能用于添加元素。通过下标访问不存在的元素会导致缓冲区溢出( buffer overflow )问题。确保下标合法的一种有效手段就是尽可能使用范围for语句。

 

v1 = v2                 //用v2中的元素拷贝替换v1中的元素

v1 = {a, b, c...}       //用列表中元素的拷贝替换v1中的元素

v1 == v2               //v1和v2相等当且仅当它们的元素数量相同且对应位置的元素值都相同

v1 != v2                //

<, <=, >, >=         //顾名思义,以字典顺序进行比较.如果两个vector 对象的容量不同,但是在相同位置上的元素值都一样,则元素较少的vector 对象小于元素较多的vector的对象; 若元素的值有区别,则vector对象的大小关系由第一对相异的元素值的大小关系决定

       只有当元素的值可比较时, vector 对象才能被比较。

 

 

迭代器介绍

类似于指针类型(参见2.3.2 节, 第47 页) ,迭代器提供了对对象的间接访问

有效的迭代器或者指向某个元素或者指向容器中尾元素的下一位置;其他所有情况都属于无效。

使用迭代器

获取迭代器是通过有迭代器的类型提供的返回迭代器的成员函数。比如,这些类型都拥有名为begin 和end 的成员, 其中begin成员负责返回指向第一个元素(或第一个字符) 的迭代器。end成员则负责返回指向容器(或string 对象) "尾元素的下一位置(one past the end )"的迭代器,end 成员返回的迭代器常被称作尾后迭代器( off-the-end iterator ) 或者简称为尾迭代器(end iterator) 。

如果容器为空则begin和end返回的是同一个迭代器,都是尾迭代器。

一般来说,如果我们不清楚(不在意)迭代器准确的类型到底是什么可使用auto 关键字定义迭代器变量。

 

标准容器迭代器的运算符

*iter 通过解引用运算符,返回迭代器iter所指元素的引用

iter -> mem 解引用iter 并获取该元素的名为mem 的成员, 等价于(*iter) .mem

++iter令iter指示容器中的下一个元素

--iter令iter指示容器中的上一个元素

iter == iter2 判断两个迭代器是否相等(不相等) ,如果两个法代器指示/指向的是同一个元

iter != iter2 或者它们是同一个容器的尾后送代器,则相等:反之,不相等

 

概念:泛型编程

       养成使用迭代器和!=(而不是<)的习惯。

 

迭代器类型

那些拥有迭代器的标准库类型使用iteratorconst_iterator来表示迭代器的类型

const iterator可读不可写它所指的元素值。相反,iterator 的对象可读可写。例:

vector<int>::iterator it;

vector<int>::const_iterator it1;

为了便于专门得到const_iterator类型的返回值, C++11=新标准引入了两个新函数,分别是cbegin和cend。

 

结合解引用和成员访问操作

为了简化解引用然后进行成员访问操作,C++语言定义了箭头运算符( -> ) 。箭头运算符把解引用和成员访问两个操作结合在一起,也就是说, iterator->mem(*iterator).mem表达的意思相同。

 

谨记,但凡使用了迭代器的循环体,都不要向迭代所属的容器添加元素

另外一个限制是任何一种可能改变 vector 对象容量的操作,比如push_back . 都会使该vector对象的迭代器失效。

 

迭代器运算

vector 和string 迭代器支持的运算

iter + n 迭代器加上一个整数值仍得一个迭代器,迭代器指示的新位置与原来相比向前移动了若干个元素。结果迭代器或者指示容器内的一个元素,或者指示容器尾元素的下一位置

iter - n 迭代器减去一个整数值仍得一个迭代器,迭代器指示的新位置与原来相比向后移动了若干个元素。结果迭代器或者指示容器内的一个元素,或者指示容器尾元素的下一位置

 

iter1 += n 迭代器加法的复合赋值语句,将iter1 加n 的结果赋给iter1

iter1 - = n 迭代器减法的复合赋值语旬,将iter1 减n 的结果赋给iter1

 

iter1 - iter2 两个迭代器相减的结果是它们之间的距离,也就是说,将运算符右侧的迭迭代器向前移动差值个元素后将得到左侧的迭代器。参与运算的两个迭代器必须指向的是同一个容然中的元素或者尾元素的下一位置,差值的类型是名为difference_type 的带符号整型数。string和vector都定义了difference_type,因为这个距离可正可负,所以difference_type是带符号类型的。

 

>、> = 、<、<= 迭代器的关系运算符,如果某迭代器指向的容器位置在另一个迭代器所指位置之前, 则说前者小于后者。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置。

 

 

 

顺序容器概述

元素在顺序容器中的顺序与其加入容器时的位置相对应。

关联容器中元素的位置由元素相关联的关键字值决定。

标准库中的顺序容器

这些容器在以下方面都有不同的性能折中:

· 向容器添加或从容器中删除元素的代价

· 非顺序访问容器中元素的代价

vector    可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢

deque     双端队列。支持快速随机访问。在头尾位置插入/删除速度很快

list         双向链表。只支持双向顺序访问。在list中任何位置进行插入/删除操作速度都很快

forward list   单向链表。只支持单向顺序访问. 在链表任何位置进行插入/删除操作速度都很快

array      固定大小数组。支持快速随机访问。不能添加或删除元素

string     与vector相似的容器,但专门用于保存字符。随机访问快。在尾部插入/删除速度快

 

选挥容器的基本原则:

· 除非你有很好的理由选择其他容器,否则应使用vector

· 如果你的程序有很多小的元素, 且空间的额外开销很重要,则不要使用list或forward list。

· 如果程序要求随机访问元素,应使用vector 或deque。

· 如果程序要求在容器的中间插入或删除元素,应使用list 或forward list 。

· 如果程序需要在头尾位置插入或删除元素,但不会在中间位置进行插入或删除操作,则使用deque 。

· 如果程序只有在读取输入时才需要在容器中间位置插入元素,随后需要随机访问元素,则

——首先,确定是否真的需要在容器中间位置添加元素。当处理输入数据时,通常可以很容易地向vector追加数据,然后再调用标准序的sort 函数来重排容器中的元素,从而避免在中问位置添加元素。

——如果必须在中间位置插入元素,考虑在输入阶段使用list,一旦输入完成,将list 中的内容拷贝到一个vector中。

 

 

 

容器库概览

一般来说,每个容器都定义在一个头文件巾, 文件名与类型名相同。容器均定义为模扳类

可以在容器中保存儿乎任何类型.但某些容器操作对元素类型有其自己的特殊要求。我们可以为不支持特定操作需求的类型定义容器,但这种情况下就只能使用那些没有特殊要求的容器操作了。

 

 

迭代器

所有迭代器都是通过解引用运算符来实现访问容器中的元素

迭代器范围

迭代器范围的概念是标准库的基础。

一个迭代器范围( iterator range ) 由一对迭代器表示,两个迭代器分别指向同一个容器中的元素或者是尾元素之后的位置( one past the last element ) 。这两个迭代器通常被称为·begin 和end.

这种元素范围被称为左闭合区间( left-inclusive interval ). 其标准数学描述为

[begin, end)

 

使用左闭合范围蕴含的编程假定

标准库使用左闭合范围是因为这种范围有三种方便的性质。假定begin 和end 构成一个合法的迭代器范围,则

· 如果begin 与end 相等,则范围为空

. 如果begin 与end 不等,则范围至少包含一个元素,且begin 指向该范围中的第一个元素

· 我们可以对begin 递增若干次,使得begin=end

这些性质意味着我们可以像下面的代码一样用一个循环来处理一个元素范围,而这是安全的:

while (begin != end) (

*begin = va1; / / 正确:范围非空,因此begin 指向一个元素

++begin ; // 移动迭代器,获取下一个元素

 

容器类型成员

每个容器都定义了多个类型(迭代器类型+类型别名),大多数容器还提供反向迭代器。简单地说,反向迭代器就是一种反向遍历容器的迭代器,与正向迭代器相比,各种操作的含义也都发生了颠倒。

例如,对一个反向法代器执行++操作,会得到上一个元素。

为了使用类型别名,我们必须显式使用其类名,例:

// iter 是通过list <string > 定义的一个迭代器类型

list <string > : : iterator iter;

 

当不需要写访问时, 应使用cbegin 和cendo

 

容器定义和初始化

 

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

将一个新容器创建为另一个容器的拷贝的方法有两种:可以直接拷贝整个容器,或者(array 除外)拷贝由一个迭代器对指定的元素范围。

当将一个容器初始化为另一个容器的拷贝时,两个容器的容器类型和元素类型都必须相同。

不过, 当传递迭代器参数来拷贝一个范围时,容器类型可以不同。而且,新容器和原容器中的元素类型也可以不同,只要能将要拷贝的元素转换为要初始化的容器的元素类型即可。例:

vector<const char*> artic1es {"a" , " an " , " the " } ;              //列表初始化,指定了容器中每个元素的值和容器的大小

forward_list<string> words (articles. begin (), articles . end()) ;   // 正确:可以将const char* 元素转换为string

 

标准库array 具有固定大小

与内置数组一样, 标准库array 的大小也是类型的一部分。当定义一个array 时,除了指定元素类型,还要指定容器大小,例:

array<int , 42> //类型为:保存42个int 的数组

如果我们对array 进行列表初始化,初始值的数目必须等于或小于array的大小。如果初始值数目小于array 的大小,则它们被用来初始化array中靠前的元素,所有剩余元素都会进行值初始化。

虽然我们不能对内置数组类型进行拷贝或对象赋值操作,但array 并无此限制:

 

赋值和swap

使用assign(仅顺序容器)

赋值运算符要求左边和右边的运算对象具有相同的类型。

顺序容器(array 除外)还定义了一个名为assign 的成员,允许我们从一个不同但相容的类型赋值,或者从容器的一个子序列赋值。assign 操作用数所指定的元素(的拷贝)替换左边容器中的所有元素。例如,我们可以用assgin实现将一个vector中的一段char *值赋予一个list 中的strìng :

list <string> names ;

vector<const char*> oldstyle ;

names oldstyle ;   //错误: 容器类型不匹配

//正确: 可以将const char* 转换为strìng

names . assign(oldstyle . cbegin() , oldstyle . cend ());

由于其旧元素被替换,因此传递给assign 的迭代器不能指向调用assign 的迭代器。

 

使用swap

swap 操作交换两个相同类型容器的内容(元素)。

除array外, swap不对任何元素进行拷贝、删除或插入操作,因此可以保证在常数时间内完成。

元素不会被移动的事实意味着,除string 外,指向容器的迭代器、引用和指针在swap 操作之后都不会失效。它们仍指向swap 操作之前所指向的那些元素。但是,在swap之后,这些元素已经属于不同的容器了。例如,假定iter在swap之前指向svecl[3]的string,那么在swap之后它指向svec2[3]的元素。与其他容器不同,对一个string调用swap会导致迭代器、引用和指针失效。

与其他容器不同. swap 两个array会真正交换它们的元素。因此交换两个array所需的时间与array 中元素的数目成正比。

       统一使用非成员版本的swap 是一个好习惯。swap(c1,c2)

 

容器大小操作

每个容器类型都有三个与大小相关的操作。成员函数size返回容器内元素的数目。empty 当slze为0时返回布尔值true,否则返回false; max_size返回一个大于或等于该类型容器所能容纳的最大元素数的值。

forward_list不支持size.

 

关系运算符

每个容器类型都支持相等运算符(=和!=);除了无序关联容器外的所有容器都支持关系运算符(> 、>= 、<、<=)。关系运算符左右两边的运算对象必须是相同类型的容器,且必须保存相同类型的元素。

比较两个容器实际上是进行元素的逐对比较

只有当其元素类型也定义了相应的比较运算符时,我们才可以使用关系运算符来比较两个容器

 

Unfinished

顺序容器操作

 

vector对象是如何增长的

 

 

标准库类型string

 

额外的string操作

 

容器适配器

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值