1.什么是容器:
容器肯定是用来盛东西的,语言中的容器主要用来存储数据。容器还可以分为顺序容器、关联容器等。
2什么是顺序容器:
顺序容器(sequence container)就是拥有单一数据类型元素组成的有序集合。其中主要的就包括vector和list。(第三种顺序容器是双端队列deque,发音deck,类似于vector,但是对首元素的插入和删除提供了特殊处理方式。)(c++ primer 3rd)
3什么是关联容器
关联容器(associative container)支持查询一个元素是否存在,并且可以有效的获取元素。两个基本的关联容器类型是map(映射)和set(集合)
4如何选择使用vector、deque还是list
主要是关注插入特性和对元素的后续访问要求。
vector是一段连续的存储区域,每个元素顺序的存储在这段连续的存储区域内。vector的随机访问效率高,就像数组一样,因为每次访问距离他的首地址偏移量都是固定的。 不在vector的末尾插入,而是在vector除末尾外的任意位置插入元素是效率很低的。
(关于list和deque参考c++ primer 3rd 223)
练习:
对于以下程序任务 vector deque 和 list 哪一个最合适 或者都不合适
(a) 为了生成随机的英文句子 从一个文件读入未知数目的单词 (vector)
(b) 读入固定数目的单词 在输入时把它们按字母顺序插入到容器中(list)
(c) 读入未知数目的单词 总是在后面插入一个新单词 从头删除下一个元素 (deque)
(d) 从文件读入未知数目的整数 对这些整数排序 然后把它们输出到标准输出 (vector)
参考:
a)选择vector,因为单词的数量未知,并且这些单词将以任意顺序被处理,而vector随机访问的效率高。
b)选择list比较合适,因为单词数量已知,并且按字母顺序存储,list的插入效率高。
c)选择deque,因为总是从后面插入,从头部删除。
d)选择vector,因为排序操作最好有随机访问的能力。
5当存储元素增长时,list和vector那个更有效率(从后面增加)?
实践中vector的效率更高一些,因为他的内存分配的空间要比实际的大一些,也就是预留出了将要增加的一部分,这样使得vector在增长的时候,效率更高一些。
#include <iostream>
#include <vector>
using std::cout;
using std::vector;
using std::endl;
int main()
{
vector<int> ivec;
cout << "ivec.size:" << ivec.size() << " "
<< "capacity:" << ivec.capacity() << endl;
for(int ix = 0; ix < 24; ++ix)
{
ivec.push_back(ix);
}
cout << "ivec.size:" << ivec.size() << " "
<< "capacity:" << ivec.capacity() << endl;
return 0;
}
这是这段代码的输出结果。
当数据结构越复杂的时候,vector的效率也就越低。
6.reserve()函数可以指定容器的大小
例如: vector<string> svec;
svec.reserve(32);//设置容器svec的size大小为0, capacity容量为32.
练习:
1)习题6.2
解释容器的容量与长度 size 之间的区别 ,为什么在连续存储的容器中需要支持容量
的概念 而非连续的容器 比如 list 则不需要?
容量是指在不需要另分内存的情况下,容器可以存放的最大元素的总量。而长度是指现在容器中实际存放的元素。
vector将元素存放在连续的内存当中,STL(标准莫板库)则是以双向串行来实现list,串行的每个节点都有两个指针,一个指向上一个节点,另一个指向下一个节点。与vector不同的是,list并不是以连续的方式存储,vector的元素一旦有一个元素超出了其容量,系统必须重新给vector分配空间,使得元素得以连续存放,由于vector涉及到container的重新分配,拷贝及释放,所以vector必须具备container的这一性质。
2)练习 6.3
为什么用指针存储大型复杂类对象的集合效率更高 而用指针存储整型对象的集合效率却比较低 ?
面对大型的复杂类对象,指针大小与等对象相比较明显小的多而且在拷贝操作上明显效率较高。当面对一系列的整形对象时,如果仍然采用指针形式,却会耗用两倍的空间:指针本身和指针指向的整形对象,这回给堆带来很大的负担。
3)练习 6.4
在下列情况下 list 和 vector 中哪一个是比较合适的容器类型, 在每一种情况下 插入 元素的数目都是未知的 请解释你的答案
(a) 整型值
(b) 指向大型 复杂类对象的指针
(c) 大型 复杂类对象
a)vector比较合适,因为vector不想list那样需要额外的指针,并且整形值的大小与list中的指针相比之下明显的小,但是有一点要注意,如果要往里面插入元素,vector要付出沉重的代价。
b)vector
c)list,因为vector涉及到容量的重新分配,拷贝以及解除分配等,使用list虽然的为指针付出代价,但是避免了额外的操作。
7.指定容器显示的长度
const int list_size = 64;
vector<int> ivector(list_size);
8.容器的初始化
每个容器都被初始化为与该类型相关的缺省默认值,对于int使用0,对于string类是用缺省构造函数初始化。
除了可以使用默认的缺省值之外,还可以指定默认的值。
例如:list <int> ilist(list_size, -1);
vector<string> svec(24, "pig");
9.通过resize()修改容器的大小
svec.resize(2*svector.size());//将深svector的大小修改为原来的两倍,容量一般也会跟正增倍。
也可以指定增加的容器的默认值:svec.resize(2*svector.size(),“pignew”);
10.容器的比较
容器的比较实际上容器内元素的比较,所以应该是相同容器之间的比较,比较的元素是从头开始,所以有点象字符串的比较。
11.能够定义的容器类型有三个限制:
1)元素类型必须支持等于操作符
2)元素类型必须支持小于操作符
3)元素类型必须支持一个缺省值
12.迭代器(iterator)
迭代器为抽象容器的访问提供了一种一般化的方法,可以连续访问容器中内容,不管事顺序容器还是关联容器。
每个容器都提供了begin()和end()函数
迭代任意容器类型的元素都可以写成以下方式:
for(iter = container.begin(); iter != container.end(); iter++)
do_something_with_element(*iter);
13.迭代器的定义:
//vector<string> svec;
vector<string>::iterator iter = svec.begin();
vector<string>::iterator iter_end = svec.end();
14.遍历const 容器必须使用const terator
(未完待续)