C++Vector使用方法

C++内置的数组支持容器的机制,可是它不支持容器抽象的语义。要解决此问题我们自己实现这种类。在标准C++中,用容器向量(vector)实现。容器向量也是一个类模板。
标准库vector类型使用须要的头文件:#include <vector>。vector 是一个类模板。不是一种数据类型,vector<int>是一种数据类型。Vector的存储空间是连续的,list不是连续存储的。

一、 定义和初始化
vector< typeName > v1;       //默认v1为空,故以下的赋值是错误的v1[0]=5;
vector<typeName>v2(v1); 或v2=v1;或vector<typeName> v2(v1.begin(), v1.end());//v2是v1的一个副本,若v1.size()>v2.size()则赋值后v2.size()被扩充为v1.size()。
vector< typeName > v3(n,i);//v3包括n个值为i的typeName类型元素
vector< typeName > v4(n); //v4含有n个值为0的元素
int a[4]={0,1,2,3,3}; vector<int> v5(a,a+5);//v5的size为5,v5被初始化为a的5个值。后一个指针要指向将被拷贝的末元素的下一位置。
vector<int> v6(v5);//v6是v5的拷贝
vector< 类型 > 标识符(最大容量,初始全部值);

 

二、 值初始化
1>     假设没有指定元素初始化式,标准库自行提供一个初始化值进行值初始化。
2>     假设保存的式含有构造函数的类类型的元素,标准库使用该类型的构造函数初始化。
3>     假设保存的式没有构造函数的类类型的元素,标准库产生一个带初始值的对象,使用这个对象进行值初始化。


三、vector对象最重要的几种操作
1. v.push_back(t)    在容器的最后加入?一个值为t的数据,容器的size变大。
    另外list有push_front()函数,在前端插入,后面的元素下标依次增大。
2. v.size()        返回容器中数据的个数,size返回对应vector类定义的size_type的值。v.resize(2*v.size)或                  

v.resize(2*v.size, 99) 将v的容量翻倍(并把新元素的值初始化为99)
3. v.empty()     推断vector是否为空
4. v[n]           返回v中位置为n的元素
5. v.insert(pointer,number, content)    向v中pointer指向的位置插入number个content的内容。
    还有v. insert(pointer, content),v.insert(pointer,a[2],a[4])将a[2]到a[4]三个元素插入。
6. v.pop_back()    删除容器的末元素,并不返回该元素。
7.v.erase(pointer1,pointer2) 删除pointer1到pointer2中间(包含pointer1所指)的元素。
   vector中删除一个元素后,此位置以后的元素都须要往前移动一个位置,尽管当前迭代器位置没有自己主动加1,
   可是因为兴许元素的顺次前移,也就相当于迭代器的自己主动指向下一个位置一样。
8. v1==v2          推断v1与v2是否相等。
9. !=、<、<=、>、>=      保持这些操作符惯有含义。
10. vector<typeName>::iterator p=v1.begin( ); p初始值指向v1的第一个元素。*p取所指向元素的值。
      对于const vector<typeName>仅仅能用vector<typeName>::const_iterator类型的指针訪问。
11.   p=v1.end( ); p指向v1的最后一个元素的下一位置。
12.v.clear()      删除容器中的全部元素。12.v.clear()      删除容器中的全部元素。

#include<algorithm>中的泛函算法
搜索算法:find() 、search() 、count() 、find_if() 、search_if() 、count_if()
分类排序:sort() 、merge()
删除算法:unique() 、remove()
生成和变异:generate() 、fill() 、transformation() 、copy()
关系算法:equal() 、min() 、max()
sort(v1.begin(),vi.begin()+v1.size/2); 对v1的前半段元素排序
list<char>::iterator pMiddle =find(cList.begin(),cList.end(),‘A‘);找到则返回被查内容第一次出现处指针,否则返回end()。
vector< typeName >::size_type x ; vector< typeName >类型的计数,可用于循环如同for(int i)


初学C++的程序猿可能会觉得vector的下标操作能够加入?元素,事实上不然:

vector<int> ivec;   // empty vector

for (vector<int>::size_type ix = 0; ix != 10; ++ix)

     ivec[ix] = ix; // disaster: ivec has no elements

上述程序试图在ivec中插入10个新元素,元素值依次为0到9的整数。可是,这里ivec是空的vector对象,并且下标仅仅能用于获取已存在的元素。

这个循环的正确写法应该是:

for (vector<int>::size_type ix = 0; ix != 10; ++ix)

     ivec.push_back(ix); // ok: adds new element with value ix


警告:必须是已存在的元素才干用下标操作符进行索引。通过下标操作进行赋值时,不会加入?不论什么元素。仅能对确知已存在的元素进行下标操作 

  

四、内存管理与效率

      1.使用reserve()函数提前设定容量大小,避免多次容量扩充操作导致效率低下。

      关于STL容器,最令人称赞的特性之中的一个就是是仅仅要不超过它们的最大大小,它们就能够自己主动增长到足以容纳你放进去的数据。(要知道这个最大值,仅仅要调用名叫max_size的成员函数。)对于vector和string,假设须要很多其它空间,就以类似realloc的思想来增长大小。vector容器支持随机訪问,因此为了提高效率,它内部使用动态数组的方式实现的。在通过 reserve() 来申请特定大小的时候总是按指数边界来增大其内部缓冲区。当进行insert或push_back等添加?元素的操作时,假设此时动态数组的内存不够用,就要动态的又一次分配当前大小的1.5~2倍的新内存区,再把原数组的内容复制过去。所以,在普通情况下,其訪问速度同一般数组,仅仅有在又一次分配发生时,其性能才会下降。正如上面的代码告诉你的那样。而进行pop_back操作时,capacity并不会由于vector容器里的元素降低而有所下降,还会维持操作之前的大小。对于vector容器来说,假设有大量的数据须要进行push_back,应当使用reserve()函数提前设定其容量大小,否则会出现很多次容量扩充操作,导致效率低下。

      reserve成员函数同意你最小化必须进行的又一次分配的次数,因而能够避免真分配的开销和迭代器/指针/引用失效。但在我解释reserve为什么能够那么做之前,让我简要介绍有时候令人困惑的四个相关成员函数。在标准容器中,仅仅有vector和string提供了全部这些函数。

(1) size()告诉你容器中有多少元素。它没有告诉你容器为它容纳的元素分配了多少内存。
(2) capacity()告诉你容器在它已经分配的内存中能够容纳多少元素。那是容器在那块内存中总共能够容纳多少元素,而不是还能够容纳多少元素。假设你想知道一个vector或string中有多少没有被占用的内存,你必须从capacity()中减去size()。假设size和capacity返回相同的值,容器中就没有剩余空间了,而下一次插入(通过insert或push_back等)会引发上面的又一次分配步骤。
(3) resize(Container::size_type n)强制把容器改为容纳n个元素。调用resize之后,size将会返回n。假设n小于当前大小,容器尾部的元素会被销毁。假设n大于当前大小,新默认构造的元素会加入?到容器尾部。假设n大于当前容量,在元素加入之前会发生又一次分配。
(4) reserve(Container::size_type n)强制容器把它的容量改为至少n,提供的n不小于当前大小。这一般强迫进行一次又一次分配,由于容量须要添加?。(假设n小于当前容量,vector忽略它,这个调用什么都不做,string可能把它的容量降低为size()和n中大的数,但string的大小没有改变。在我的经验中,使用reserve来从一个string中修整多余容量一般不如使用“交换技巧”,那是条款17的主题。)

     这个简单介绍表示了仅仅要有元素须要插入并且容器的容量不足时就会发生又一次分配(包含它们维护的原始内存分配和回收,对象的拷贝和析构和迭代器、指针和引用的失效)。所以,避免又一次分配的关键是使用reserve尽快把容器的容量设置为足够大,最好在容器被构造之后立马进行。

比如,假定你想建立一个容纳1-1000值的vector<int>。没有使用reserve,你能够像这样来做:

vector<int> v;
for (int i = 1; i <= 1000; ++i) v.push_back(i);
在大多数STL实现中,这段代码在循环过程中将会导致2到10次又一次分配。(10这个数没什么奇怪的。记住vector在又一次分配发生时一般把容量翻倍,而1000约等于210。)

把代码改为使用reserve,我们得到这个:

vector<int> v;
v.reserve(1000);
for (int i = 1; i <= 1000; ++i) v.push_back(i);
这在循环中不会发生又一次分配。

在大小和容量之间的关系让我们能够预言什么时候插入将引起vector或string运行又一次分配,并且,能够预言什么时候插入会使指向容器中的迭代器、指针和引用失效。比如,给出这段代码,

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。