STL与泛型编程<二>:Vector

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时还未对其容器中元素赋值时进行reverse
vector<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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值