类型
一段能对任何标准容器工作的代码,好好体会。
template
typename C::value_type about_vector::sum(const C& c)
{
typename C::value_type s = 0;
typename C::const_iterator p = c.begin();
while(p != c.end())
{
s += *p;
++p;
}
return s;
}
迭代器
迭代器可以看作是指向容器元素的指针,我们可以这样理解,但我们不应当依赖于此,最佳的获取
vector
内某个元素指针的方式是
&v[i]。一般我们总应该使用安全性更强的const_iterator。
反向迭代器我们也不能忽视,很多时候能利用它写出更好的代码。
base()返回一个iterator,指向reverse_iterator指向位置的后一位置。
注意:
iterator
和
reverse_iterator
不是同一类型。
元素的访问
同其他容器相比,vector可以高效的按任何顺序访问元素,支持下标和at()两种方式,
下标提供不加检查的访问,
at()
则要做范围检查,并在下标越界时抛出
out_of_range异常。
提供不检查的方式是为了和数组相匹配,另外,我们可以在一个快速机制上建立一套(带检查的)安全功能,却无法在慢速机制上构建一套快速功能。
front()
和back()分别返回对首元素和末元素的引用。注意:是引用,而begin()返回的是一个迭代器。我们可以认为front()是第一个元素,begin()是指向第一个元素的指针。但back()是最后一个元素,
end()
是指向最后一个元素下一个位置。
构造函数
vector提供了对任意元素的快速访问,但修改其大小的代价是昂贵的,故在构建vector常常给出一个初始化大小,如果不指定大小,则默认为0。也可以通过初始元素的集合隐式给定。
如果某个类型没有默认构造函数,在没有显式为每个函数提供值的情况下,我们不能创建以这个类型为元素的向量。
复制赋值运算符函数和拷贝构造函数拷贝vector所有元素,对于有许多元素的vector来说,代价是很高的,所以vector通常采用引用传递。
对于
assign()
函数,则是为了实现一些与多参数构造函数相对应的功能。
注意:赋值将完全改变一个向量里的全部元素,所有的老元素被删除,新元素插入。
堆栈操作
我们常把vector看做一种可以直接通过下标访问元素的紧凑数据结构。
在
vector
末尾增加一个元素可能成为代价高昂的操作,因为或许需要为保存它而分配额外的内存。因此我们使用时,需要保证由于重复的堆栈操作所引起的与增长有关的开销是很少出现的(比如通过使用reserve()预留空间)。
为什么要在
vector
上实现类似堆栈的操作呢?一个原因是为了实现stack,另一个原因是需要以递增的方式创建一个vector。
如果只是简单的将输入数据放入vector中,采用构造函数也能实现上例中的功能。但当需要对输入适当的做一些处理时,就需要使用push_back()了。
vector不会上溢,除非申请不到内存,但使用pop_back()时,可能会导致下溢,下溢的效果是无定义的。
表操作
注意插入都是在pos位置之前,返回的迭代器指向新插入的元素;而擦除则是在pos位置上,返回的迭代器指向被删除元素的后一个位置。
在执行
insert()
或
erase()
操作后,原本指向
vector
中一个元素的迭代器可能指向其他元素,甚至有可能指向
vector
之外的某一位置,原因在于这些操作可能导致改变vector大小,从而需要重新分配vector的存储。这时我们可以采用保存下标的方式代替保存迭代器。
下面代码是危险的。
template <typename T>
void
about_vector::duplicate_elements(vector& c)
{
for(vector::iterator i = c.begin(); i != c.end(); i ++)
{
c.insert(i, *i);
}
}
可修改为:
template
<typename T>
void
about_vector::duplicate_elements(vector& c)
{
for(vector::iterator i = c.begin(); i != c.end(); i += 2)
{
i = c.insert(i, *i);//新增成功后,返回指向新增元素位置的迭代器
}
}
此外,我们可以通过预留空间,来避免重新分配存储。同时,预留空间,也可以提高程序效率。
元素定位
最常见还是采用对迭代器做加减来定位元素,但也并不是所有容器都支持+运算,例如,list就不支持。如果需要对一个list::iterator加7,则只能反复进行++。
大小(size)和容量(capacity)
在表操作中,我们提到了reserve()妙用,在这要注意它与resize()的区别。前者针对capacity且不进行初始化,后者针对size且进行初始化。
减少vector的大小并不会减少其容量,但剩余空间更多了。如果需要将内存还给系统,重新赋值即可。
reserve()
和
capacity()
仅限于
vector
以及与它类似的紧凑型容器。像list这样的就没有。
vector
标准库提供了专门化的vector,作为bool类型的紧凑vector。在这里,每个元素只占一个二进制位。但它具有通常的vector操作,包括下标和迭代器。由于指针无法对小于一个字节的内存单元寻址,所以vector::iterator就可能不是指针,不能认为是bool*。
原笔记为word格式,还有很多从英文原版书上截下来的相关代码图片,但粘贴不了。不知有没有快速粘贴word中图片的方法?