STL与泛型编程<二>:Vector

来源:互联网 发布:怎么和淘宝客服联系 编辑:程序博客网 时间:2024/06/16 22:18

Vector模拟一个动态数组,其内存模型如下
这里写图片描述

Vector的能力

vector将元素复制到内部的dynamic array中,是一个有序群集;vector支持随机存取,vector迭代器是一个随机存取迭代器 ,所以对任何一个STL算法都可以。

大小(size)和容量(capacity)

为什么vector比容器的通用操作多了个capacity()?就是因为
vector优异性能就是配置比其容纳的元素所需更多的内存。其中capacity()返回vector实际能够容纳的元素数量,如果超越了这个数量,vector就有必要重新配置内部存储器
vector的容量很重要,主要有以下两点
1. 一旦内存重新分配,和vector元素相关的所有references,pointers,iterators都会失效
2. 内存重新配置很耗时间
当然,可以使用reserve()保存适当容量,避免重新分配,且reverse()操作不会改变容器的size和元素

#include <iostream>#include <vector>using namespace std;int main(void){    int arr1[] = {1,2,3};    vector<int> v1(arr1,arr1+sizeof(arr1)/sizeof(arr1[0]));    vector<int>::const_iterator it = v1.begin();    cout << "size:" << v1.size() << endl; //3    cout << "capacity:" << v1.capacity() << endl; //3    cout << "begin iterator :" << *it << endl; //1    v1.reserve(80); //reserve后重新分配分配内存空间,所有references,pointers,iterators都会失效    cout << "size:" << v1.size() << endl; //3    cout << "capacity:" << v1.capacity() << endl;//80    cout << "begin iterator :" << *it; //一个杂乱的数字    return 0;} /*如上注释,所有reserve一般在元素还未装入容器中时就调用了,一般也这样使用,在刚创建vector时还未对其容器中元素赋值时进行reversevector<int> v;v.reserve(80);...//赋值操作*/

注意:vector不能使用reserve()来缩减容量,如果reserve()的参数比当前vector的容量还小,则不会产生任何作用。这里有个间接缩减vector容量的小窍门,如下代码

再看一个例子缩减容量的

#include <iostream>#include <vector>using namespace std;template <typename T>void shrinkCapacity(vector<T>& v){    vector<T> tmp(v);    v.swap(tmp);    }int main(void){    int arr1[] = {1,2,3};    vector<int> v1;    v1.reserve(30);    v1.assign(arr1,arr1+sizeof(arr1)/sizeof(arr1[0]));    vector<int>::const_iterator it = v1.begin();    cout << "before shrinkCapacity :" << endl;    cout << "size:" << v1.size() << endl;//3    cout << "capacity:" << v1.capacity() << endl;//30    cout << "begin iterator :" << *it << endl;//1    cout << endl;    cout << "after shrinkCapacity :" << endl;    shrinkCapacity(v1);    cout << "capacity:" << v1.capacity() << endl;//3    cout << "begin iterator :" << *it << endl;  //出现的值奇怪    //所以应该更新一下才行    it = v1.begin();    cout << "begin iterator :" << *it << endl;//1    return 0;} 

不过注意,swap()后原先所有的references,pointers,iterators都会失效。
甚至可以直接调用

vector<T>(v).swap(v);  //其实就是创建一个临时变量与v交换/*vector<T>(v); 创建一个临时变量,并用v进初始化*/

另外一个避免重新分配内存的是,初始化期间就想构造函数传递附加参数

vector<T> v(5);    //创建一个有5个值得vector,但是会调用5次default构造函数

如果这样做仅是为了保留足够内存,建议还是使用reserve()。
事实上,为了防止内存破碎,在很多STL版本中,容量的增大幅度很大,即使你不调用reserve(),当你第一次安插元素时,也会一口气配置整块内存(如2K)。
既然vector的容量不会缩减,删除元素,其references,pointers,iterators也会继续有效,然而安插操作可能使其失效(因为安插可能导致vector重新分配空间)

Vector的操作函数

  • 构造、拷贝和析构
    这里写图片描述
  • 非变动性操作
    这里写图片描述
  • 赋值
    这里写图片描述
    上图列出了“将新元素赋值给vector,将旧元素全部移除”的方法,所有赋值操作都可能会调用元素型别的default构造函数,copy构造函数 ,assignment操作符和或析构函数
  • 元素存取
    这里写图片描述
    只有at会进行越界检查,其他函数不做检查
  • 迭代器相关函数
    这里写图片描述
  • 安插和移除元素
    这里写图片描述
    需要注意的是:安插或移除元素都会使“作用点”之后的元素的references,pointers,iterators失效。安插操作若是引得vectro重新分配内存,那么该容器身上的所有references,pointers,iterators都会失效
    vector没有提供删除与val相等的所有元素,因此需要借助算法。

栗子,删除与val相等的第一个元素

#include <iostream>#include <vector>#include <algorithm>using namespace std;void print(const vector<int>& v){    vector<int>::const_iterator it = v.begin();    for (; it!=v.end(); ++it)        cout << *it << " ";    cout << endl;   }int main(void){    double val = 3;    int arr[] = {1,2,3,4,3,25,3};    vector<int> v(arr,arr+sizeof(arr)/sizeof(arr[0]));    print(v);    vector<int>::iterator it;   //刚开始设置为const_iterator    it = find(v.begin(),v.end(),val);    if (it != v.end())    v.erase(it);    print(v);    return 0;}/*犯了一个错误:见标识处,而erase()里的参数中的iterator不是const型的*/

将vector当作一般Arrays使用

简单的说只要你需要动态数组,你就可以使用vector。
看一个例子

#include <iostream>#include <vector>#include <cstring>using namespace std;void print(const vector<char>& v){    for (int i=0; i<v.size(); ++i)        cout << v[i] << " ";    cout << endl;   }int main(void){    vector<char> v;    v.resize(40);    strcpy(&v[0],"hello");    cout << &v[0] << endl;    //cout << v.begin() << endl; 错误,might work, but not portable    print(v);    return 0;}/*如今vector就像一个连续数组一样,可以通过其首地址&v[0]来打印字符串的值。*/

注意:千万不要把迭代器当作第一元素的地址来传递,vector迭代器是由实作版本定义的,也许不是个一般指针
还有如果要存字符串数组的话,建议用vector.

补充

swap还可以交换元素的值,不止是交换两个容器

#include <iostream>#include <vector>#include <cstring>using namespace std;int main(void){    vector<int> v;    v.push_back(2);    v.push_back(3);    swap(v[0],v[1]);    cout << v[0];    cout << v[1];}

总结

vector比容器通用的操作多出以下几个成员函数

capacity(); reserve();assign(n,elem);assign(beg,end);push_back();pop_back();resize();

可以看出:
1. 多出的函数有不少是和容量有关的,所以说弄清楚vector的容量很重要
2. 另外的就是和元素插入删除有关的函数,主要是和vector特性有关,都只在尾部进行相应动作
3. 当然vector也可以在中间插入,但是成本大,不划算
4. vector当容量不足时,一般的实作版本会将容量扩充一倍

错误

由于时常回过头来看看,经常发现问题并记录下来。
- 关于reserve()操作,先看一个例子

#include <iostream>#include <vector>#include <algorithm>#include <functional>#include <iterator>using namespace std;int main(void){    vector<int> col;    col.reserve(10);    col[0] = 1;    col[1] = 2;    cout << col[0] << " " << " " << col[1] << endl;    copy(col.begin(),col.end(),ostream_iterator<int>(cout," "));    return 0;}/*这代码在DevC++(gcc)和vs08下都能编译通过,但是DevC++下能运行,结果:1 2(当然最后一句打印容器的值是没有值的)在 vs08下运行会出错 */ 

原因:reserve()操作不会改变容器的元素和容器的size(可通过size()操作返回),而operator[]操作不做边界检查,可能越界,因此编译不会出错,但是运行会出错。
总结:注意STL的大多数操作都不提供边界检查,因此需要特定注意,尤其在对迭代器进行操作时

1 0
原创粉丝点击