第9章 顺序容器
【57】9.2创建和初始化一个vector对象有四种方式,为每种方式提供一个例子,并解释每个例子生成的vector对象包含什么值。
答:1)分配指定数目的元素,并对这些元素进行值初始化;
vector<int>ivec(10); //ivec包含10个0值元素
2)分配指定数目的元素,并将这些元素初始化为指定值:
vector<int>ivec(10,1);//ivec包含10个值为1的元素
3)将vector对象初始化为一段元素的副本:
intia[10]={1,2,3,4,5,6,7,8,9,10};
vector<int>ivec(ia,ia+10); //ivec包含10个元素,值分别为1~10;
4)将一个vector对象初始化为另一个vector对象的副本:
vector<int>ivec1(10,1);
vector<int>ivec2(ivec1);//ivec2包含10个值为1的元素
【58】9.4定义一个list对象来存储deque对象,该deque对象存放int型元素。
答:list< deque<int> > lst;
注意必须用空格隔开两个相邻的>符号,否则系统会认为>>是单个符号,为右移操作符,从而导致编译时错误。
【59】9.5为什么我们不可以使用容器来存储iostream对象?
答:因为容器元素类型必须支持赋值操作及复制,而iostream类型不支持赋值和复制。
【60】9.6假设有一个名为Foo的类,这个类没有定义默认构造函数,但提供了一个需要int型参数的构造函数。定义一个存放Foo的list对象,该对象有10个元素。
答:list<Foo> fooList(10,1); //各元素均初始化为1
【61】9.7下面的程序错在哪里?
list<int>lst1;
list<int>::iteratoriter1=lst1.begin(),iter2=lst1.end();
while(iter1<iter2){…}
答:错误在于list容器的迭代器不支持关系操作,可更正为while(iter1!=iter2){…}
【62】9.10下列迭代器的用法哪些(如果有)是错误的?
constvector<int> ivec(10);
vector<string>svec(10);
list<int>ilist(10);
a)vector<int>:;iteratorit=ivec.begin();
错误,因为返回的迭代器的类型为const vector<int>,不能用来对类型为vector<int>的迭代器it进行初始化。
b)list<int>::iteratorit=ilist.begin()+2;
错误,因为list容器的迭代器不支持算术运算;
c)vector<string>::iteratorit=&svec[0];
d)for(vector<string>::iteratorit=svec.begin();it!=0;++it){…}
错误,因为循环条件中迭代器it与0进行比较,导致运行时内存访问非法的错误,应该将it!=0改为it!=svec.end();
【63】9.16int型的vector容器应该使用什么类型的索引?
答:int型的vector容器应该使用的索引类型为vector<int>::size_type
【64】9.17读取存放string对象的list容器时,应该使用什么类型?
答:list<string>::iterator和list<string>::const_iterator实现顺序读取;
list<string>::reverse_iterator和list<string>::const_reverse_iterator实现逆序读取;
【65】9.21假设c1和c2都是容器,下列用法给c1和c2的元素类型带来什么约束?
if(c1<c2)
(如果有的话)对c1和c2的约束又是什么?
答:对c1和c2的元素类型的约束为:类型必须相同且都支持<操作。
对c1和c2的约束为:类型必须相同且都支持<操作。
【66】9.22已知容器vec存放了25个元素,那么vec.resize(100)操作实现了什么功能?若再做操作vec.resize(10),实现的又是什么功能?
答:vec.resize(100)操作使容器中包含100个元素:前25个元素保持原值,后75个元素采用值初始化。
若再做操作vec.resize(10),则使容器中包含10个元素,只保留前10个元素,后面的元素被删除。
【67】9.23使用只带一个长度参数的resize操作对元素类型有什么要求(如果有的话)?
答:因为只带有一个长度参数的resize操作对新添加的元素进行值初始化,所以元素类型如果是类类型,则该类必须显式提供默认构造函数,或者该类不显式提供任何构造函数以便使用编译器自动合成的默认构造函数。
【68】9.27编写程序处理一个string类型的list容器,在该容器中寻找一个特殊值,如果找到,则将它删除。
答:#include <iostream>
#include<list>
#include<string>
usingnamespace std;
intmain()
{
list<string>alst;
stringstr;
cout<<””Entersome strings:”<<endl;
while(cin>>str) slst.push_back(str);
cin.clear();
cout<<”Entera string that you want to:”<<endl;
cin>>str;
for(list<string>:;iteratoriter=slst.begin();iter!=slst.end();++iter)
{
if(*iter==str){
iter=slst.erase(iter);
--iter;
}
return0;
}
注意,在删除元素后迭代器会失效,因此一定要对迭代器重新赋值。另外,erase函数返回一个迭代器,指向被删除元素的下一个元素。因为在for语句头中要对迭代器加1,所以在it语句中将迭代器减1,以免遗漏需处理的元素。
【69】9.29解释vector的容量和长度之间的区别。为什么在连续存储元素的容器中需要支持“容量“的概念?而非连续的容器,如list,则不需要?
答:vector的容量是指容器在必须分配新存储空间之前可以存储的元素总数,而长度是指容器当前拥有的元素个数。
对于连续存储元素的容器而言,容器中的元素是连续存储的。当在容器内添加一个元素时,如果容器中已经没有空间容纳新的元素,则为了保持元素的连续性存储必须重新分配存储空间,用来存放原来的元素以及新添加的元素。首先将存放在旧存储空间中的元素复制到新存储空间里,接着插入新元素,最后撤销旧的存储空间。如果在每次添加新元素时,都要这样分配和撤销内存空间,其性能将会很慢。为了提高性能,连续存储元素的容器实际分配的容量要比当前所需的空间多一些,预留了一些额外的存储区,用于存放新添加的元素,使得不必为每个新元素重新分配容器。所以,在连续存储元素的容器中需要支持“容量“的概念。
而对于不连续存储元素的容器,不存在这样的内存分配问题。例如,在list容器中添加一个元素,标准库只需创建一个新元素,然后将该新元素连接到已存在的链表中,不需要重新分配存储空间,也不必复制任何已存在的元素。所以,这类容器不需要支持“容量“的概念。
【70】9.33对于下列程序任务,采用哪种容器实现最合适?
a)从一个文件中读入未知数目的单词,以生成英文句子;
因为单词数量未知,且需要以非确定的顺序处理这些单词,所以采用vector实现最合适,因为vector支持随机访问。
b)读入固定数目的单词,在输入时将它们按字母顺序插入到容器中。
采用list实现最合适,因为需要在容器的任意位置插入元素
c)读入未知数目的单词,总是在容器尾部插入新单词,从容器首部删除下一个值。
采用deque实现最合适,因为总是在容器尾部插入新单词,从容器首部删除下一个值
d)从一个文件中读入未知数目的整数。对于这些整数排序,然后把它们输出到标准输出设备。
如果一边输入一边排序,则采用list实现最合适。因为在读入时需要在容器的任意位置插入元素(从而实现排序);如果先读入所有整数,再进行排序,则采用vector最合适,因为进行排序最好有随机访问能力。
【71】9.36如何用vector<char>容器初始化string对象
答:vector<char> cvec(10,’a’);
stringstr(cvec.begin(),cvec.end());