《C++Primer》第九章 顺序容器——笔记

1、容器的基本操作

下图列出了,所有容器(除特殊标注的容器外)都支持的【基本】操作:
在这里插入图片描述

2、迭代器

2.1 迭代器支持的 公共 操作如下:

在这里插入图片描述

  • forward_list迭代器不支持递减运算符
2.2 支持迭代器的容器如下:

在这里插入图片描述

2.3 vectorstring的【迭代器】支持的运算如下:

在这里插入图片描述

  • list的【迭代器】不支持 关系运算符,只支持 ++ -- == !=,也不支持+ -一个整数
  • forward_list的迭代器也不支持 关系运算符,只支持++ == !=,连--都不支持,更不支持+ -一个整数在这里插入图片描述在这里插入图片描述

3、创建容器对象

有如下几种定义和初始化方式:
在这里插入图片描述

【值初始化】:由容器中所装【元素的类型】决定如何初始化,如果是int,则初始化0;如果是某种类类型,则元素由类默认初始化。

3.1 拷贝初始化

拷贝初始化有两种:

  • 一种是直接【拷贝整个容器】,这要求,两者的【容器类型】和【元素类型】都一致。
  • 一种是把某个容器【一段区间内的元素】拷贝到新容器,这时,两者的【容器类型】和【元素类型】都可以不一致。只要【元素类型】可以转换成【新容器的元素类型】即可。

因此,下面的拷贝初始化中,中间两条会报错:
在这里插入图片描述

3.2 大小参数初始化

有两种:

  • 一种接收两个参数,第一个是大小n,第二个是值t,则容器里有n个元素t
  • 一种只接收一个参数:大小n,容器初始容纳n个元素。元素的值根据元素的类型进行初始化,如果是int则初始化为0,如果元素是 类 类型,就用默认构造函数初始化,没有默认构造函数?那不行!!!必须人为指定初始值,也就是用第一种方法初始化。

在这里插入图片描述
目前已知的关联容器有:map,set

3.3 特殊的array

  • 必须指定大小,像这样array<int, 10> arr;
  • 如果元素是 类 类型,则这个类【必须有默认构造函数】
  • 和数组不同,可以用一个array给另一个array赋值
    array<int, 3> arr1{ 1, 2, 3 };
    array<int, 3> arr2 = arr1;

4、容器赋值运算

在这里插入图片描述

4.1 assign()替换

两种用法:

  • 传入一对迭代器范围,用范围内元素的拷贝替换容器原有的元素。拷贝的元素要能转换成目标容器中的元素。
  • 传入一个大小n,元素值t,用nt替换原有的元素。

在这里插入图片描述

4.2 swap()

交换的容器 类型和元素类型都得一样。

swap的原理】:除arrat外,swap不对任何元素进行拷贝,删除或插入,他交换的是两个容器内指向他们的数据结构的指针,而元素在内存中的地址保持不变。

因此,除string外,只想容器的迭代器、引用和指针在swap操作之后都不会失效。他们仍然指向原来的元素,只是已经(物是人非了)属于不同的容器了。

【两个例外】:

  • string调用swap会导致迭代器、引用和指针失效。
  • swap会真正交换array中元素的值,所以交换array所需的时间和里面的元素数量成正比。对于arrayswap操作后,指针引用和迭代器所绑定的元素保持不变,但元素值已经与另一个array对应的元素值交换了。
    swap交换的两个array的大小必须一样。

5、容器的关系运算符

每个容器都支持 ==!= ,除了【无序关联容器】外的所有容器都支持 < > >= <=。关系运算符左右 容器类型,元素类型【都得相同】。
  • 大小相等,元素相同,则==
  • 大小不同,小的是大的 的前缀, 小的 < 大的
  • 大小不同,无前缀,大小取决于第一个不相同的元素

在这里插入图片描述
【注:两个容器的容量(capacity)不会影响关系运算符的判定】

6、向顺序容器【添加】元素

表头列出了不支持部分操作的容器。
在这里插入图片描述

  • 向一个vector stringdeque插入元素会使所有指向容器的迭代器、引用和指针失效。
  • 调用insert将元素插入到vector deque string的任何位置都是合法的,但是插入到有的位置会很耗时

  • 虽然vector不支持push_front(),但可以通过insert实现同样的功能。

  • insert还有两个重载版本,第一个,可以在指定位置插入n个值为t的元素;第二个,可以在指定位置插入 指定迭代器区间内 的元素。

6.1 insert的返回值

新标准下,insert的返回值指向第一个和新加入元素的迭代器,如果insert函数没有插入任何元素,则返回调用insert函数的【第一个参数】。

这代表什么???我们可以用insert的返回值,在【一个位置】反复插入元素。
也能实现类似push_front()的功能,代码如下:

list<string> list1;
auto it = list.begin();
while (cin >> word)
	it = list.insert(it, word);    等价于调用push_front,实现了头插

6.2 直接【构造新元素】,并插入

新标准引入三个新成员emplace_frontemplace_backemplace
他们分别对应push_frontpush_backinsert

但不同的是,他们都是根据传入的参数,现调【构造函数】,创建一个对象,压入容器中。
如下代码所示:

class X
{
public:
	X() = default;
	X(int a) { }
	X(int a, double b) { }
	X(int a, double b, char c) { }
};
int main()
{
	vector<X> xvec;
	
	下面四句分别对应四种构造函数,emplace使用这些构造函数创建对象,压入xvec容器中。
	xvec.emplace_back();
	xvec.emplace_back(1);
	xvec.emplace_back(1, 3.14);
	xvec.emplace_back(1, 3.14, 'a');

	xvec.push_back( X(1, 3.14, 'a') );	显式创建了一个临时匿名对象,压入名为xvec的容器中
	
	cout << xvec.size() << endl;		程序会输出 5
	return 0;
}

emplace成员使用传入的参数在容器管理的内存空间中直接构造元素。

传递给emplace函数的参数必须与元素类型的【构造函数相匹配】。

7、【访问】元素

在这里插入图片描述

  • 不能递减forward_list的迭代器,且forward_list不支持back()
  • 注意back()front()[]at()返回的【都是】引用,若容器是const的,则返回const的引用

8、删除元素

在这里插入图片描述

  • 删除 deque中【除首尾位置之外的】任何元素都会使所有迭代器、引用和指针失效。
  • 指向vectorstring中【删除点之后】位置的迭代器、引用和指针都会失效。
  • vectorstring不支持pop_front
  • forward_list不支持pop_back
  • erase返回被删元素之后的迭代器

9、特殊的forward_list

forward_list是单向链表,所以性质比较特殊。
在这里插入图片描述

  • 当然啦,forward_list也有begin()end()成员,也返回指向首元素和尾后元素的迭代器。
  • forward没有普通的insertemplaceerase成员
  • forward_list中插入、删除元素既需要改元素的迭代器,也需要前驱迭代器。
  • erase_aftererase的返回值意义相同,而insert_afterinsert的返回值意义不同

10、改变容器大小

在这里插入图片描述

  • 同样的,如果元素是 类 类型,则要么提供初始值,要么该类有默认构造函数。

11、容器操作使迭代器(指针,引用)失效问题

11.1 添加造成的影响

  • 对于vectorstring:如果添加导致【存储空间被重新分配】,则全部迭代器,指针和引用失效。如果为重新分配,指向【插入位置之前】的迭代器,指针和引用仍有效,之后的全部失效。
  • 对于deque
    • 插入到【首尾之外】的位置,【所有迭代器,指针和引用】失效。
    • 若插入到【首尾位置】,所有【迭代器失效】,只想存在的元素的【引用和指针不会失效】。
  • 对于listforward_list:随便添加,所有迭代器(包括尾后和首前),指针和引用都有效

11.2 删除造成的影响

  • 对于listforward_list:除删除元素的,其他所有迭代器,指针和引用都有效。
  • 对于deque
    • 在【首尾之外】的位置删除,所有迭代器,指针和引用【都失效】;
    • 删除【尾】元素,尾后迭代器失效,其他【都不受影响】。
    • 删除【首】元素,首迭代器失效,其他【都不受影响】。
  • 对于vectorstring:被删元素之前的迭代器,指针和引用仍有效,之后的全失效。
必须保证每次改变容器的操作之后都正确的【重新定位迭代器】。

在这里插入图片描述
更不能将这个end用作while循环结束条件。

12、vector的内部实现 简析

12.1 管理容量的成员函数

在这里插入图片描述

reserve(n)

调用reserve只会增大容器的内存空间,也就是说,若reserve请求的大小小于当前容量,不会退回内存空间。事实上,编译器实际分配的内存空间,可能会比reserve请求的还大。

shrink_to_fit

请求,没错,是请求编译器缩小容器的内存空间到当前的size()大小,当然编译器可以忽略这个请求。也就是说调用shrink_to_fit也不一定保证退回内存空间。

resize

resize只改变容器的元素数目,并不改变容量

只要插入操作没有超过vector的容量,vector就不能重新分配空间。

在这里插入图片描述

13、string的特殊操作

13.1 构造和拷贝string

在这里插入图片描述
当从一个const char*创建string时,指针指向的数组必须以空字符结尾,拷贝操作遇到空字符时停止。要是const char*不是以空字符结尾的,那你必须还要提供一个计数值,告诉拷贝多少个。
如果你既不传计数值,也不以空字符结尾,或者传的计数值大于数组长度,都会报错。如下所示:

const char* c1 = "hello world!!!";
char c2[] = { 'H', 'i' };
string s1(c1);
string s2(c2, 2);
string s3(c2);					这就不对了,虽然编译器不会报错,但输出s3会输出乱码
								编译器会一直往后找,直到找到 \0 为止

substr操作:

substr操作返回一个string,它是调用该成员的string的一部分或全部的拷贝。可以传递给substr一个可选的开始位置和计数值。开始位置不能超过string大小,计数值可以无限大,但顶多拷贝到最后一个字符。

string s2 = s1.substr(2, 1e5);

stringinserterase能够接收下标:

s.insert(size(), 5, '!');		 在s末尾插入5个感叹号
s.erase(s.size() - 5, 5);		 从s删除最后5个字符

C++是存在尾后下标的,和end()指向相同的位置。

能接受C风格字符数组的insertassign版本:

const char* cp = "Stately, plump Buck";
s.assign(cp, 7);					s == "Stately"
s.insert(s.size(), cp + 7);			s == "stately, plump Buck"

对于调用insert的代码:我们的意图是将字符插入到s[size()]处(不存在的)元素之前的位置。在此例中,我们将cp开始的7个字符(至多到结尾空字符之前)拷贝到s 中。

我们也可以指定将来自其他string或子字符串的字符插入到当前string中或【赋予】当前string

strign s = "some string", s2 = "some other string";
s.insert(0, s2);					在s中位置0之前插入s2的拷贝
s.insert(0, s2, 0, s2.size());		在s[0]之前插入s2中s2[0]开始的s2.size()个字符
appendreplace函数:详请参考《C++Primer》第323页

13.2 字符串搜索操作《C++Primer》第325页

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

  • 搜索操作返回的都是unsigned类型,所以建议使用auto接收函数的返回值

13.3 compare函数

在这里插入图片描述

13.4 数字 与 字符串 转换

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

14、容器适配器

《C++Primer》第329页

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值