c++ primer学习笔记 第九章

第九章 顺序容器

一个容器就是一些特定类型对象的集合。顺序容器为程序员提供了元素存储和访问顺序的能力,这种顺序不依赖于元素的值,只与元素加入容器时的位置相对应。而关联容器则根据关键字的值来存储元素。

1.概述

string和vector将元素保存在连续的内存空间,所以支持连续随机访问,在尾部插入数据快,在其他地方插入数据慢,因为要挪动很多。

list和forward_list不支持元素随机访问,访问一个元素只能遍历,但在任意位置添加删除都很快。

deque是双端队列,支持快速随机访问,在两端添加删除快。

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

**通常vector是最好的选择。**23333

2.容器库概览(所有容器都适用的操作)

每个容器定义在一个头文件内。

容器均被定义为模板类(模板分为类模板和函数模板)。

1.可以保存的类型

顺序容器几乎可以保存任意类型的元素,容器的元素可以是另一个容器。

vector<vector> lines;`

2.迭代器

迭代器有公共的接口。**如果一个迭代器提供某个操作,那么所有提供相同操作的迭代器对这个操作的实现方式都相同。**例如标准容器类型的迭代器都允许访问容器中的元素,而所有迭代器都是通过解引用运算符来实现这个操作,类似标准库迭代器都定义了递增运算符。

**迭代器范围:**一对迭代器,指向同一个容器中的元素或者是尾后元素,而且end不在begin之前,那begin和end构成一个迭代器范围。迭代器范围是左闭合区间,即[begin,end).

3.容器类型成员

比如size_type, iterator, const_iterator这些。

反向迭代器的++得到上一个元素。

3.begin和end成员

a.begin(); //返回第一个元素

a.rbegin(); //返回反向迭代器

a.cbegin(); //返回const迭代器

4.容器定义和初始化

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

可以将一个容器初始化为另一个容器的拷贝;可以列表初始化;只有顺序容器的构造函数才接受初始化中的大小参数。

array初始化需要考虑大小,array<int 10>,以及内置数组类型虽然不能拷贝,但是array可以的哦(不愧是你)

5.赋值和swap

可以直接赋值,要求运算对象具有相同的类型。或者用assign(array除外),将一个容器的子序列赋值给另一个容器。

swap(a,b)交换a,b中的元素,对于array,交换时真正交换了元素,对于其它的只是交换了内部数据结构,也就是说iter在交换前指向a[3],交换后指向b[3]。

6.容器大小操作

成员函数size返回容器中元素数目(forward_list不支持),empty在size为0时返回true,max_size返回一个大于等于该类型容器能容纳最大元素数的值。

7.关系运算符

比较两个容器实际上是进行元素的逐个比较,容器的关系运算符使用元素的关系运算符完成比较。

每个容器类型都支持==和!=,除了无序关联容器其他的都支持>, <, >=, <=。

3.顺序容器操作

顺序容器和关联容器的不同之处在于二者组织元素的方式,这些不同之处直接关系到元素如何存储,访问,添加及删除。

1.向顺序容器添加元素

向vector, string, deque中插入元素会使得所有指向容器的迭代器,引用和指针都失效。因为会引起整个对象存储空间的重新分配,需要分配新的内存,并将元素从旧的空间移动到新的空间。

1.push_back(vector, string, deque, list)

将一个元素追加到尾部。

当我们使用对象初始化容器或者把对象插入容器时,实际上放的都是拷贝,随后容器中元素的任何改变都不会影响到原始对象。

2.push_front(deque, list, forward_list)

将元素插入头部。可以实现插入4 3 2 1这种逆序。

3.insert(vector, string, deque, list, forward_list的insert比较特殊)

将元素插入到所指定位置之前。insert成员函数接受一个迭代器作为其第一个参数。

a.insert(iter, "hello")

在vector, string, deque中可能会很耗时间的哦

第一个参数后时要插入的内容,可以是一个字面值,也可以是元素数目和值,或者一对迭代器指定的范围(但是不能是目标容器的迭代器)。

新标准下,接受元素个数或范围的insert返回指向第一个新加入元素的迭代器,旧标准为void。

list<string> 1st;

auto iter=1st.begin();

while(cin>>word)

iter=1st.insert(iter,word);//iter一直指向第一个元素,相当于push_front

4.emplace

empalce_front, emplace, emplace_back分别对应push_front, insert, push_back,但是是构造而不是拷贝元素,用的时候看书吧。。。

2.访问元素

front成员函数和back成员函数。

c.front()相当于*c.begin(), c.back()相当于对last迭代器递减后解引用。forward_list不支持back成员函数。

访问成员函数(front,back,下标和at)返回的是引用,如果使用auto来保存函数的返回值,记得定义为引用类型。

不能访问空容器。

auto &v=c.back();

提供快速随机访问的容器都提供下标访问,下标运算符接受一个下标参数,返回容器中该位置元素的引用。或者使用at成员函数。

3.删除元素

删除deque中除首尾元素之外的元素会使所有迭代器,引用和指针失效;指向vector或string中删除点之后位置的迭代器,引用和指针会失效。

删除的成员函数有pop_back(), pop_front(), erase,clear。

4.特殊的forward_list操作

当添加或者删除一个元素需要访问前面的元素的链接,但是这个使单向链表。

forward_list未定义insert, emplace,和erase,而是定义了insert_after, emplace_after和erase_after。

5.改变容器大小resize

resize成员函数。

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

添加元素:

vector或者string,存储空间被重新分配了则迭代器引用指针都失效,未重新分配指向插入位置之后的迭代器引用指针失效;

deque,插入首尾位置迭代器会失效,但是指向存在元素的指针引用不失效,插入其他位置都会失效;

list和forward_list仍有效。

删除元素:

vector和string,指向删除元素之后的迭代器引用指针失效;

deque,删除尾元素则尾后迭代器失效,其它不影响;

list和forward_list不影响。

添加/删除vector, string, deque元素的循环程序必须考虑失效问题,所以必须保证每个循环步中都更新迭代器,引用或者指针。如果循环调用的是insert或者erase,那么更新迭代器很容易,因为这些操作都返回迭代器。

不要保存end的返回值,每次操作之后重新调用end多好啊,速度也快。

4.vector对象是如何增长的

vector将元素连续存储,添加元素每次都要重新分配内存空间。所以可以预留一些空间,虽然每次要移动所有元素,但是比重新分配内存高效。

1.管理容量的成员函数

c.capacity(); //不重新分配内存的话,c可以保存多少元素

c.reserve(); //分配至少能容纳n个元素的内存空间,不改变容器中元素的数量

2.capacity和size

size是指已经保存的元素数目,capacity是指在不分配内存空间的前提下最多可以保存多少元素

我笑死了shrink_to_fit只是一个请求,标准库不保证退还内存,那要他干嘛???

5.额外的string操作

标准库string类型定义了大量函数。

1.构造string的其它方法

2.substr操作

s.substr(pos,n); //返回一个string,包含s中从pos开始的n个字符的拷贝。

3.改变string的其它办法(append,replace)

insert和eraase除了可以接受迭代器指明位置,也可以用下标。

s.insert(s.size(), 5, ‘!’); //在s末尾插入5个感叹号

插入的可以是字符也可以是string。

append成员函数在string末尾直接插入。

s.append("hjkk");

replace成员函数相当于先erase再insert。

s.replace(11,3,"ghui"); //在s的11位置处删除后面的三个元素在加上ghui

p324有具体重载的操作。

4.string搜索操作find

string提供了6和搜索函数,每个有四个重载。

搜索返回string::size_type值,表示匹配发生位置的下标,是一个unsigned类型,因此不用int或者其它有符号的类型来保存,可以用auto;

**搜索失败返回名为string::npos的static成员,**标准库将npos定义为一个const string::size_type类型,并初始化为-1。

最简单的函数为find,大小写敏感。

查找第一次/最后一次出现的位置。查找任何一个字符第一次/最后一次出现的位置。查找第一个/最后一个不在其中的字符。

可以指定从哪里搜索,可以逆向搜索(rfind)

5.compare函数

类似于c中的strcmp。具体看书吧

6.数值转换

字符1和字符5在一起表示数值15

int i=42;

string s=to_string(i); //转换为字符形式

double d=stod(s); //转换为数字

要转换为数值的string中第一个非空白字符必须是数值中可能出现的字符(+,-,.,0123456789)

如果不能转换。函数抛出invalid argument异常,如果转换得到的数值无法用任何类型表示,抛出out_of_range的异常。

6.容器适配器

适配器是标准库中的一个概念,本质上是一种机制,让某种事物的行为看起来像另一种事物。

容器,迭代器和函数都有适配器,一个容器适配器接受一种已有的容器类型,让其行为看起来像一种不同的类型。

除了顺序容器外,标准库还定义了三个顺序容器适配器:stack, queue和ppriority_queue。stack接受一个顺序容器(除了array和forward_list),让其操作看起来像一个stack一样。

1.定义适配器

所有适配器都要求容器具有添加,删除及访问尾元素的能力,所以不能用array和forward_list。

stack要求push_back, pop_back,back,所以可以使用除以上两种的其它;

queue要求back, push_back, front, push_front,所以可以构造于list或者deque;

ppriority_queue除了front, push_back, pop_back外好需要随机访问能力,所以可以构造于vector或者deque。

每个容器适配器都基于底层容器类型的操作定义了自己的特殊操作,我们只可以调用适配器操作,而不能使用底层容器类型的操作,例如stack的不能调用push_back,只能调用push。

2.栈适配器

stack类型定义在stack头文件中,默认基于deque实现。

先进后出。

3.队列适配器

queue和ppriority_queue适配器定义在queue头文件中。

先进先出。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值