c++标准库——vector容器

c++标准库——vector容器
四月 8th, 2010 by ewangplay

1 vectors容器的能力

size和capacity的区别:vector容器除了提供通用的三个跟元素数目有关的函数size(), empty()和max_size(),另外还提供了一个独特的capacity()函数,那么这个capacity()和size()的区别是什么呢?

  • size(): 返回容器中实际的元素数目。
  • capacity(): 返回容器在重新扩充内存之前可能容纳的最多元素数目。
  • max_size(): 返回容器在当前系统框架下可能容纳的最多元素数目。
我们可以理解为vector容器会预留一段内存空间,这个内存空间可能会容纳10个元素,但目前只插入了5个元素,那么size()返回的就是5,而capacity()返回的就是10。当vector目前的内存空间不足以容纳新的元素时,就会自动扩充内存空间,而这个过程会产生两点影响:
  • 重新分配内存会使所有针对容器中元素的引用、指针和iterator失效。
  • 重新分配内存会浪费时间,降低性能。


为了不至于使扩充内存的现象频繁发生,我们可以根据需要提前为vecotor容器预留足够大的内存空间,可以使用reserve()函数来预留元素的内存空间:

std::vector<int> col1;    //create a empty vector
col1.reserve(80);           //reserve memory for 80 elements


另外也可以通过为vector的构造函数传递参数来预留内存空间:

std::vector<T> col1(10);    //create a vector and initialize it with 10 elements

                                        //calls 10 times the default constructor of type T
当然,使用这种方法的前提是容器元素类型必须提供缺省的构造函数。但是对于复杂的数据类型,这种初始化要占用一定的时间。如果你的目的仅仅是为了预留一定的元素空间,优先使用reserve()函数,它只是预留内存空间,并不进行元素的初始化。

通过下面简单的例子我们就可以看的更清楚:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<int> col1;
    col1.reserve(10);
    col1.push_back(2);
    col1.push_back(5);
    cout << "col1 size:" << col1.size() << endl;
    cout << "col1 capacity:" << col1.capacity() <<endl;
    cout << endl;

    vector<int> col2(10);
    cout <<"col2 size:" << col2.size() << endl;
    cout << "col2 capacity:" <<col2.capacity() <<endl;
}
运行结果如下:
col1 size:2
col2 capacity:10


col2 size:10
col2 capacity:10


值得注意的是,vector容器可以扩充内存空间,却不可以缩小内存空间。给reserve()函数传递一个小于当前容器capacity值的参数是no-op(无效果操作)。正是因为vector容器不能缩小内存空间,所以当删除和修改元素时,可以保证被操作元素之前的针对该容器元素的所有引用、指针、iterator仍然有效;但是插入操作就不能保证这种有效性了。
虽然不能直接缩小vector容器的capacity,但通过辅助手段还是可以一定程度缩小这个capacity值。当使用swap()交换两个vector容器的内容时,其对应的capacity也相应交换。基于这个理论,我们可以设计下面的模板函数,用来在保留容器元素的情况下缩小容器的capacity值:

template <typename T>
void shrinkCapacity(std::vector<T>& v)
{   
    std::vector<T> tmp(v);    //copy elements into a new vector
    v.swap(tmp);                   //swap internal vector data
}

你也可以把上面函数浓缩成一个语句:

std::vector<T>(v).swap(v);    //shrink capacity of vector for type T


2 vector容器的操作

Element access
vector容器为元素的直接访问提供了四个操作符:

  • c.at(idx): 返回idx索引处的元素,进行索引值的有效性检查,如果超出了有效范围,抛出out_of_range异常。
  • c[idx]: 返回idx索引处的元素,不进行索引值的有效性检查。
  • c.front(): 返回第一个元素,不进行存在性检查。
  • c.back(): 返回最后一个元素,不进行存在性检查。
跟数组类似,vector容器的元素索引值也从0开始计数,所以最后一个元素的索引值是size()-1。对于nonconstant(非常量)vector容器,上面四个操作符返回对应元素的引用(reference),可以通过这些操作符直接修改元素的值。值得注意的是,只有at()操作符进行索引值有效性检查,如果无效抛出out_of_range异常,其他三个操作符都不进行有效性检查,如果idx无效或者容器为空,那么[], front(), back()的行为没有定义(不确定):
std::vector<int> col1;    //create a new empty vector
col1[5] = 3;                  //runtime error? undefined behavior
cout << col1.front();      //runtime error? undefined behavior
col1.at(5) = 3;               //throws out_of_range exception

所以,为了程序的健壮性,你必须自己进行有效性检查:

std::vector<int> col1; 
......
if (col1.size() > 5)
{
    col1[5] = 3;
}
if (!col1.empty()) 
{
    cout << col1.front();
}


3 vector容器作为普通数组
虽然c++标准中没有明确地指出vector容器的元素是否排列在连续的内存空间中,但事实上这一点是得到保证的。所以下面语句对于有效的索引值i结果是为true:

&v[i] == &v[0] + i

这个保证意味着我们可以把vector容器作为普通的动态数组来使用,比如:

std::vector<char> v;
v.resize(41);            //make room for 41 characters (include '\0')
strcpy(&v[0], "hello, world!");
printf("%s\n", &v[0]);

当然在使用之前,我们要使用resize()或者reserve()来保证动态数组有足够的内存空间来容纳复制的数据。而且,当作为动态字符数组来使用时,要特别注意字符串后面要以'\0'字符结束。

需要注意的是,不可以把第一个元素的Iterator作为第一个元素的地址来使用,因为iterator跟实现相关,它的含义可能跟普通指针大相径庭。
printf("%s\n", v.begin());    //error!
printf("%s\n", &v[0]);        //ok!

4 vector容器的异常处理

针对vector容器的异常处理,c++标准库保证下面事项:

  • 使用push_back()插入元素发生异常,保证对容器内容没有影响。
  • 使用insert()插入元素发生异常,如果插入元素的复制操作(拷贝构造函数和赋值操作)不会抛出异常,保证对容器内容没有影响。
  • pop_back()不会抛出异常。
  • erase()和clear()不会抛出异常。
  • swap()不会抛出异常。
  • 如果容器元素是简单数据类型,不使用复杂的c++特性,那么针对容器的操作要么成功,要么不会产生影响。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值