vector类的RAII
template<typename T, typename A>
void vector<T,A>::reserve(int newalloc)
{
if (newalloc<=space) return; //从不减少分配的内存
T* p = alloc.allocate(newalloc); //分配新空间
for (int i = 0; i < sz; ++i) alloc.construct(&p[i], elem[i]); //拷贝
for (int i = 0; i < sz; ++i) alloc.destroy(&elem[i]); //销毁
alloc.deallocate(elem, space); //释放旧空间
elem = p;
space = newalloc;
}
一个简单的文本编辑器
列表最重要的性质就是可以在不移动元素的情况下对其进行插入或删除操作。
我们把文本文件看成由一系列“行”组成,并用list<Line>进行表示,其中Line是一个vector<char>。
能够在不移动现有链接的情况下在链表中加入新的链表是非常重要的,其逻辑上的原因是我们可能
正用迭代器指向这些现有链接,或用指针(以及引用)指向这些链接中的对象。
处理行
我们应该如何判定文档中什么是“一行”呢?有三种显然的选择:
1、靠换行符(例如‘、n’)来判断。
2、用某种“自然的”标点(如.)采用某种方法分析文档。
3、把所有超过给定长度(如50个字符)的行都分成两行。
using Line = vector<char>; //一行就是一个字符vector
struct Document
{
lsit<Line> line; //一个文档就是一个行的list
Document() {line.push_back(Line{});}
};
读取文档并分行的操作可以按照如下方式完成:
istream& operator>>(istream& is, Document& d)
{
for (char ch; is.get(ch);)
{
d.line.back().push_back(ch); //添加字符
if(ch == '\n')
d.line.push_back(Line{}); //添加一行
}
if (d.line.back().size()) d.line.push_back(Line{}); //添加最后的空行
return is;
}
vector和list都有一个back()成员函数,返回指向尾元素的引用。
遍历
我们为Document类专门定义一个迭代器:
class Text_iterator //在行内跟踪行和字符位置
{
list<Line>::iterator In;
Line::iterator pos;
public:
//迭代器从行 || 中位置PP处的字符开始
Text_iterator(list<Line>::iterator ||, Line::iterator pp)
:In{ll}, pos{pp} {}
char& operator&*(){reurn *pos;}
Text_iterator& operator++();
bool operator==(const Text_iterator& other) const
{return In==other.In && pos==other.pos;}
bool operator != (const Text_iterator& other) const
{reurn !(*this==other);}
};
vector、list和string
四种存储字符序列的方法
char[] (字符数组);
vector<char>;
string;
list<char>
elem[]: 不知道它自己的大小。
vector[elem]:
string: 其元素保证在内存中连续存储。string可以扩展。
list[elem]:
如果你在程序中需要使用很多大对象,而且会在很多地方(用迭代器或指针)指向它们,则应考虑使用list.
list与vector的不同之处在于,我们没有移动任何元素,q始终是有效的。
list<char>与其他三种容器相比需要至少3倍的存储空间----在PC上,一个list<char>
需要12个字节来保存每个元素,而vector<char>只需要1个字节。
实际上,vector设计思路之一就是对push_back()这样的“内存操作”进行优化,
而string并没有。
vector和string逻辑的主要差异在于vector几乎可以用于任何元素类型,而只有在处理字符时我们
才考虑string.。总之,只有当需要进行字符串操作时才考虑使用string,其他情况下,就是vector好了。
调整vector类达到STL版本的功能
调整内置数组达到STL版本的功能
容器概览
STL提供了一些容器:
vector 连续存储的元素序列;应用做默认容器
list 双向链表;当你希望在不移动现有元素的情况下完成对元素的插入和删除时使用
deque list和vector的交叉;除非你对算法和计算机体系结构知识非常精通,否则不要使用它
map 平衡有序树;当你需要按值访问元素时使用它。
multimap 平衡有序树,可以包含同一个key的多个拷贝;当你需要按值访问元素时使用它。
unordered_map 哈希表;一种优化的map;当映射规模很大、对性能要求很高且可以设计出好的哈希函数时使用它
unordered_multimap
set 平衡有序树;当你需要追踪单个值时使用它
multiset 可以包含同一个key的多个拷贝的平衡有序树;当你需要追踪单个值是使用它
array 大小固定的数组,不存在内置数组所存在的大部分问题
什么是容器?
一个STL容器:
是一个元素序列[begin():end()].
提供拷贝元素的拷贝操作。拷贝可以通过赋值操作或拷贝构造函数来实现。
其元素类型命名为value_type。
迭代器类别
输入迭代器: 我们可以用++向前移动,用*读取元素值。istream提供的就是这类迭代器。
输出迭代器: 我们可以用++向前移动,用*写入元素值。ostream提供的就是这类迭代器。
前向迭代器: 我们可以反复用++向前移动,用*读写元素。
双向迭代器: 我们可以反复用++向前移动,用--向后移动,用*读写元素。
随机访问迭代器: 我们可以反复用++向前移动,用--向后移动,用*或[]读写元素。
我们可以对随机访问迭代器进行下标操作,用+向迭代器加上一个整数,用-向迭代器减去一个整数。
我们可以通过对指向同一序列的两个迭代器进行减法操作来得到他们的距离。vector提供的就是这类迭代器。