C++primer学习笔记及作业答案之第九章

笔记:

9.1 顺序容器概述

如果不确定使用哪种类型的容器,那么可以使用vector和list的公共操作,使用迭代器,不适用下标操作。

list 的迭代器不支持比较运算。list不支持<运算,只支持递增递减、==以及!=操作。

9.2 容器库概述

每个容器都定义了一个默认构造函数。

只有顺序容器(不包括array)的构造函数才能接受参数大小。

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

只有顺序容器的构造函数才接受大小参数,关联容器并不支持。

虽然我们不能对内置数组类型进行拷贝或对象赋值操作,但是array并没有这个限制。

赋值相关运算会导致指向左边容器内部的迭代器、引用和指针失效,而swap不会。(array和string情况除外。)

swap:

swap只是交换了两个容器的内部数据结构,除了array外,swap不对任何元素进行拷贝、删除或插入操作,因此可以保证在常数时间内完成。

除string外,指向容器的迭代器、引用和指针在swap操作之后都不会失效。

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

9.3顺序容器操作

emplace函数在容器中直接构造元素,传递给emplace函数的参数必须与元素类型的构造函数相匹配。

提供快速随机访问的容器(vector,string,deque,array),也提供下标运算符。

forward_list有一个首前迭代器,即指向首元素之前的那个不存在的元素,和尾后迭代器一样。当在尾后插入元素,以及使用erase_after的时候,要使用首前迭代器。

如果在一个循环中插入/删除deque、string、或vector中的元素,不要缓存end返回的迭代器。

list和forward_list与其他容器的一个不同是,迭代器不支持加减运算,即迭代器不支持加减一个数,究其原因,链表中元素并非在内存中连续存储,因此无法通过地址的加减在元素间远距离移动。

9.4 vector对象是如何增长的

在virtual studio2013上,vector对象的增长好像不是以翻倍的速度进行的。

而在windows上用g++进行编译,vector对象就是翻倍的增加容量的。

9.5 额外的string操作

很多string的操作函数,等以后用的时候可以再仔细回来研究。

9.6 容器适配器

栈默认基于deque实现,也可以在list或vector之上实现。

课后习题:

练习 9.1:对于下面的程序任务,vector、deque和 list 哪种容器最为适合?解释你的选择的理由。如果没有哪一种容器优于其他容器,也请解释理由。
(a) 读取固定数量的单词,将它们按字典序插入到容器中。我们将在下一章中看到,关联容器更适合这个问题。
(b) 读取未知数量的单词,总是将单词插入到末尾。删除操作在头部进行。
(c) 从一个文件读取未知数量的整数。将这些数排序,然后将它们打印到标准输出。

答:(a)“按字典顺序插入到容器中”意味着进行插入排序操作,从而需要在容器内部频繁进行插入操作,vector在尾部之外的位置插入和删除元素都很慢,deque在头尾之外的位置插入和删除元素很慢,而list在任何位置插入、删除速度都很快。因此这个任务选择list更为适合。

(b)由于需要在头、尾分别进行插入、删除等操作,因此将vector排除在外,deque和list都可以达到很好的性能。如果还需要频繁进行随机访问,则deque更好。

(c)由于整数占用空间很小,且快速的排序算法需频繁随机访问元素,将list排除在外。由于无需在头部进行插入、删除操作,因此使用vector即可,无需使用deque。

练习 9.2:定义一个list 对象,其元素类型是int 的deque。

list<deque<int>> a;

练习 9.3:构成迭代器范围的迭代器有何限制?

答:两个迭代器begin和end必须指向同一个容器中的元素,或者是容器最后一个元素之后的位置;而且,对begin反复进行递增操作,可以保证达到end,即end不在begin之前。

练习 9.4:编写函数, 接受一对指向vector<int> 的迭代器和一个int 值。在两个迭代器指定的范围中查找给定的值,返回一个布尔值来指出是否找到。

// 练习 9.4
#include <iostream>
#include <vector>

using namespace std;
typedef vector<int>::iterator asv; //类型别名定义

bool myFind(asv it1, asv it2, int num)
{
	for (; it1 != it2; ++it1)
	{
		if (*it1 == num)
		{
			return true;
		}
	}
	return false;
}

int main()
{
	vector<int> vi;
	//给vector对象赋值
	for (int i = 0; i < 10; ++i)
	{
		vi.push_back(i);
	}
	vector<int>::iterator it1 = vi.begin();
	vector<int>::iterator it2 = vi.end();
	int val = 41;
	cout << myFind(it1, it2, val) << endl;

	system("pause");
	return 0;
}

练习 9.5:重写上一题的函数,返回一个迭代器指向找到的元素。注意,程序必须处理未找到给定值的情况。

// 练习 9.5
#include <iostream>
#include <vector>

using namespace std;
typedef vector<int>::iterator asv; //类型别名定义

asv myFind(asv it1, asv it2, int num)
{
	for (; it1 != it2; ++it1)
	{
		if (*it1 == num)
		{
			return it1;
		}
	}
	return it2;   //搜索失败返回尾迭代器
}

int main()
{
	vector<int> vi;
	//给vector对象赋值
	for (int i = 0; i < 10; ++i)
	{
		vi.push_back(i);
	}
	vector<int>::iterator it1 = vi.begin();
	vector<int>::iterator it2 = vi.end();
	int val = 6;
	//输出目标值迭代器与首元素迭代器之间的差值
	cout << myFind(it1, it2, val) - it1 << endl;

	system("pause");
	return 0;
}

练习 9.6:下面程序有何错误?你应该如何修改它。

list<int> lst1;
list<int>::iterator iter1 = lst1.begin(),
                    iter2 = lst1.end();
while(iter1 < iter2)   /* ... */

list不支持<运算,只支持递增递减、==以及!=操作。可改成iter1 != iter2。list中元素是以链表的方式进行存储的。

练习 9.7:为了索引int 的vector 中的元素,应该使用什么类型?

答:使用迭代器类型vector<int>::iterator来索引int的vector中的元素。索引使用迭代器。

练习 9.8:为了读取string 的list 中的元素,应该使用什么类型?如果写入list,又该使用什么类型?

答:为了读取string的list中的元素,应使用list<string>::valus_type,写入数据需要引用类型,使用list<string>::reference。读取使用元素类型。

练习 9.9:begin 和cbegin 两个函数有什么不同?

答:cbegin是C++新标准引入来的,用来与auto结合使用。它返回指向容器第一个元素的const迭代器,可以用来只读地访问容器,但不能对容器元素进行修改。因此,当不需要写访问时,应该使用cbegin。

begin则是被重载过的,有两个版本:其中一个是const成员函数,也返回const迭代器;另一个则返回普通迭代器,可以对容器元素进行修改。

练习 9.10:下面4 个对象分别是什么类型?

vector<int> v1;
const vector<int> v2;
auto it1 = v1.begin(), it2 = v2.begin();
auto it3 = v1.cbegin(), it4 = v2.cbegin();
//it1是iterator,it2是const_iterator
//it3是const_iterator,it4是const_iterator

练习 9.11:对6 种创建和初始化vector 对象的方法,每一种都给出一个实例。解释每个vector 包含什么值。

(1)vector<int> ilist; 
//默认初始化,vector为空,容器中尚未有元素。

(2)vector<int> ilist2(ilist); 
//ilist2初始化为ilist的拷贝,ilist必须与ilist2类型相同。

(3)vector<in> ilist = {1,2,3.0,4,5,6,7};
//初始化为列表中元素的拷贝,列表中的元素类型必须与ilist的元素类型相容,这里必须是与整型相容的数值类型。

(4)vector<int> ilist3(ilist.begin()+2, ilist.end()-1);
//ilist3初始化为两个迭代器指定范围中的元素的拷贝,范围中的元素类型必须与ilist的元素类型相容,
//这里ilist3被初始化为{3, 4, 5, 6}.

(5)vector<int> ilist4(7); 
//默认值初始化,ilist4中将包含7个元素,每个元素进行缺省的值初始化,对于int,也就是被赋值为0.

(6)vector<int> ilist5(7, 3); 
//指定值初始化,ilist5被初始化为包含7个值为3的int。

练习 9.12:对于接受一个容器创建其拷贝的构造函数,和接受两个迭代器创建拷贝的构造函数, 解释它们的不同。

答:接受一个已有容器的构造函数会拷贝此容器中的所有元素,这样,初始化完成后,我们得到此容器的一个一模一样的拷贝、当我们确实需要一个容器的完整拷贝时,这种初始化方式非常方便。这要求两个容器的类型以及其元素类型必须匹配。

当我们不需要已有容器中的全部元素,而只是想拷贝其中一部分元素时,可使用接受两个迭代器的构造函数。这不要求容器类型相同,新容器和原容器中的元素类型也可以不同,只要能将要拷贝的元素转换成要初始化的容器的元素类型即可。

练习 9.13:如何从一个list<int> 初始化一个vector<double> ?从一个vector<int>又该如何创建?编写代码验证你的答案。

// 练习 9.13

#include <iostream>
#include <vector>
#include <list>

using namespace std;

int main()
{
	list<int> ilist = { 1, 2, 3, 4, 5, 6, 7 };
	vector<int> ivec =
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值