现在的位置: 首页 > 综合 > 正文

STL容器之vector

2013年08月09日 ⁄ 综合 ⁄ 共 4524字 ⁄ 字号 评论关闭

Vector总览

vectorC++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库。vector之所以被认为是一个容器,是因为它能够像容器一样存放各种类型的对象,简单地说,vector是一个能够存放任意类型的动态数组,能够增加和压缩数据。缺点就是她的异常处理机制不完善,这点你在接下来的内容将会看见很多。

为了可以使用vector,必须在你的头文件中包含下面的代码:

#include <vector>

vector属于std命名域的,因此需要通过命名限定,如下完成你的代码:

using std::vector;

或者是使用using namespace std来限定名称空间。

Vector的定义方式:

vector<Elem> c

vector <Elem> c1(c2)

vector <Elem> c(n)

vector <Elem> c(n, elem)

vector <Elem> c(beg,end)

c.~ vector <Elem>()

创建一个空的vector

复制一个vector

创建一个vector,含有n个数据,数据均已缺省构造产生

创建一个含有nelem拷贝的vector

创建一个以[beg;end)区间的vector

销毁所有数据,释放内存。

由图表可以看出vector的初始化方式是多样的,可以指定大小,可以进行已知的复制等等。

如:

Vector<int> vec1(10);

创建含有10个元素的容器,相当于C语言中的 int vec[10];

Vector<float> vec2(10,1)

创建含有10个元素的容器,每个的初始值设置为1; 

Vector<float> vec3(vec2);

创建一个容器vec3并把vec2复制给他,那么他们拥有一样的长度和数据。

Int a[10]={1,2,3,4,5,6,7,8,9,10};

Vector<int> vec4(a,a+9);

创建一个容器并把数组aa+9区间内的元素赋值给他

Vector提供了很多其他的接口函数,下面列出他的接口函数

c.assign(beg,end)

c.assign(n,elem)

[beg; end)区间中的数据赋值给c

nelem的拷贝赋值给c

c.at(idx)

传回索引idx所指的数据,如果idx越界,抛出out_of_range

c.back()

传回最后一个数据,不检查这个数据是否存在。

c.begin()

传回迭代器重的可一个数据。

c.capacity()

返回容器中数据个数。

c.clear()

移除容器中所有数据。

c.empty()

判断容器是否为空。

c.end()

指向迭代器中的最后一个数据地址。

c.erase(pos)

c.erase(beg,end)

删除pos位置的数据,传回下一个数据的位置。

删除[beg,end)区间的数据,传回下一个数据的位置

c.front()

传回地一个数据。

get_allocator

使用构造函数返回一个拷贝。

c.insert(pos,elem) 

c.insert(pos,n,elem)

c.insert(pos,beg,end)

pos位置插入一个elem拷贝,传回新数据位置。

pos位置插入nelem数据。无返回值。

pos位置插入在[beg,end)区间的数据。无返回值。

c.max_size()

返回容器中最大数据的数量。

c.pop_back()

删除最后一个数据。

c.push_back(elem)

在尾部加入一个数据。

c.rbegin()

传回一个逆向队列的第一个数据。

c.rend()

传回一个逆向队列的最后一个数据的下一个位置。

c.resize(num)

重新指定队列的长度。

c.reserve()

保留适当的容量。

c.size()

返回容器中实际数据的个数。

c1.swap(c2) 

swap(c1,c2)

c1c2元素互换。

同上操作。

注:以上函数均可在附带的C++标准参考文档中找到。

接下来我将对其中几个方面做一些说明:

1:vector的大小(size())和内存容量(capacity())问题。

Vectorsize()是指的是当前元素数量,而capacity()是能够容纳的元素数量,如果超过这个值,那么所有的迭代器,引用,指针都会失效,而且重新分配内存是非常耗时的,为了避免重新分配内存带来的时间消耗,你可以在容器初始化的时候给他足够的容量。或者在中途使用reverse函数重置他的大小。

Vector<int> vec5(10);

For(int i=1;i<50;i++)

{

If(vec5.size()<=10)

Vec5.push_back(i);

Else

Vec5.reverse(2*vec5.size());

}

当容量不够的时候重置容量,增加容器的长度。但是这里涉及到一个问题,一旦重置了长度,vector原来的迭代器都会失效,因为重置长度会发生内存改变,原来迭代器所指的地方已经不是新容器的地址了,所以要注意重新定义迭代器再使用,别使用重置之前的。

2:关于vector容器取元素的问题。

c.front()  c.back()   c.at(idx)这三个函数。

由于C语言中的动态数组使用索引来访问元素,为与之兼容,vector也重载了这个操作符,允许像数组一样来操作容器,但是STL中提供了另外一个函数 at()函数也能达到相同的目的。

大家在使用C语言的时候是否出现过这种情况的:

     Int a[10];

   `````````````````

Cout<<a[10]<<endl

即使我们非常小心,但是数组越界的错误也不能完全避免,这将导致程序崩溃

at函数则附加了更好的越界检查,如果越界将会跑出越界的异常,程序不至于崩溃,这是我们推荐使用的函数。

vector<int> v;

v.reserve(10);

for(int i=0; i<7; i++)

    v.push_back(i);

try

{

 int iVal1 = v[7];  // 不抛出异常

 int iVal2 = v.at(7); // 抛出下标越界异常。

}

catch(const exception& e)

{

 cout << e.what();

}

对于frontback这两个函数,返回开始和最后元素,但是在使用他们的时候你务必确保容器不为空,因为他们没有异常检测的功能,为空将导致未定义错误。

3vector的插入和删除元素,inserterase函数

这里我首先说明一点,对于插入和删除操作,如果你的程序主要是利用这两个操作,那么你最好别选用vector容器,如果你非要利用vector来执行很多的插入删除操作,那么我相信你浪费的效率将是非常可观的

对于这两种函数,vector中本身定义了两个比较方便在尾部删除和插入的元素,它们是push_back()

pop_back()函数,效率还是挺高的。

但是在容器中间删除和插入,他的效率就有点力不从心了,因为大多数vector的实现版本都是模拟数组,一旦删除一个元素,后面的所有元素都要向前移一位。

执行插入删除需要注意的是:必须指向一个合法的位置执行,不能在空容器中删除。

c.erase(pos)  pos为迭代器;

c.erase(beg,end)  参数为一个区间。

从中可以看到对于vector,他并没有提供删除某一具体数值的方法,比如删除容器中等于5的元素。

这个是用就需要我们的算法来辅助了:

std::vector<int> coll;

std::vector<int>::iterator it;

it=find(coll.begin(),coll.end(),5);

if(it!=coll.end())

coll.erase(it);

也就是先找到元素的位置还进行删除。

对于insert函数:

c.insert(pos,elem) 

c.insert(pos,n,elem)

c.insert(pos,beg,end)

这些函数其实都是很简单的,我下面要说的还是那个老问题,迭代器失效的问题。

 先看这段代码;

根据代码的意思,可以大概的出来他想干什么,他的容器中由于数字是123456789之间夹了很多个5,他想把其中的5都删除掉,只留下123456789While循环里面的意思很明显,一次循环,为5的就删,不为5的就跳过。运行结果如下:

 

什么原因?下面我来仔细的分析一下:

循环第一步的时候没问题,第二步遇见一个5,然后删除掉,也没问题,可到了第三步就出问题咯,刚删了一个5,对于数组型的容器,他的后面元素依次向前移动一位,end()这个迭代器已经失效,指向一个未知的位置,理解不过来的可以画图看一下。

其实如果要删除里面所有的5,可以用remove函数:

 

运行结果:

哎呀,为什么又是这样勒?

带着这个问题,我们通过查找remove函数可以知道,实际上他为了效率并没有进行删除操作,只是把需要删除的元素的后一位向前移一位(前提是后一位不是要删除的对象,如果是,则继续后推),把需要删除的覆盖掉。依次类推,直到移到最后。然后他返回逻辑上的新终点的迭代器。所以程序中运用的end()是最原来的末位置,知道了这些,我们改写一下代码:

运行结果:

从上面几个例子可以看出,迭代器失效的问题和函数的实作细节是很重要的问题,上面那个程序还好end()有一个位置,如果没有的话 恐怕也崩溃了吧。下面我们可以下一个定义:

凡是在vector中执行了插入和删除操作,那么他之后的那些迭代器都会失效,不能再继续使用。当然在尾部插入删除是安全的。

大家务必记得,关于迭代器失效是STL中的一个难点,以后还会陆续的遇到,希望注意。关于vector的实例我就不举了,书上很多。

3vectorresize()和reserve()函数的区别这两个函数的具体区别。

可能很多初学者不明白这两个函数在实作中的区别,下面我将带你了解他们。

在参考文档里可以看到,resize()是重置容器的容量,reserve是保留一定的容量,换句话说,如果你在使用时候reserve的时候,只是空出那么多内存,但是只是预留的,暂时不属于容器可以访问的范围,而resize函数则是增加内存,并可以访问,举例说明:

.vector<int> myVec

.myVec.reserve( 100 );     // 新元素还没有构造,

                          // 此时不能用[]访问元素

.for (int i = 0; i < 100; i++ )
..{

  myVec.push_back( i ); //新元素这时才构造

.}

myVec.resize( 102 );      // 用元素的默认构造函数构造了两个新的元素

.myVec[100] = 1;           //直接操作新元素

.myVec[101] = 2;

运行结果:

再看reserve;

结果:

这下应该够明白了吧,这就是我们在vector刚创建的时候就调用reserve保留一定的内存,防止他由于重新分配内存带来的效率消耗。

 

抱歉!评论已关闭.