基于C++11的Vector容器分析

Vector的基本概念

  1. 本身是一个基于dynamic arrray的一个抽象概念

  1. 使用前必须包含#include<vector>头文件,在此头文件中,类型vector是一个定义于namespace std内的template

namespce std{
     template<typename T,typename Allocator=allocator<T>>
             class vector;
}

第一个参数为任意类型,第二个参数用以定义内存模型,默认参数为C++标准库提供的allocator

Vector的能力

  1. 基于一种动态数组的概念,因此元素之间总是存在着一定的顺序,所以vector是一种有序集合

  1. 基于有序集合,在vector末尾添加或者删除元素的效率都相当好,但是在除末尾以外的位置修改元素效率就不怎么样了(因为要维护每一个元素的相对位置,修改一个要改变其后面所有元素的位置)

  1. 类似于数组的成员下标,vector支持随机访问迭代器,适用于所有的STL算法

大小(size)和容量(capacity)

概念理解:大小是指实际元素的多少,容量是指总共所能够元素的大小即size<=capacity

对于操作大小和容量的函数:

size()      //返回实际元素的多少、
empty()     //返回容器是否为空的判断
capacity()  //返回容器能够储存的元素量

Vector的容器之所以重要,有两个原因

  1. 一旦内存重新分配,vector容器中的所有reference,pointer,iterator都会失效

  1. 内存分配很耗费时间

两个解决容量分配时间过多的方法:

  1. 通过调用reserve()函数保留适当容量

std::vector<T> v;
v.reserve(80);
  1. 通过在初始化期间传入额外实参,构建足够的内存空间

std::vector<T>v(10);

这两种方法的区别:通过调用reserve()函数得到的就是为了保留足够空间而已,但通过初始化时来分配空间必须要额外需要一个对应T类型的default构造函数

如果要缩减容量以符合当前元素个数,可以使用C++11引入的shrink_to_fit()函数

v.shrink_to_fit();

注:该要求不具有强制力,因此你不能够期望之后的v.capacity()==v.size()会获得true

Vector的构造函数和析构函数

      vector<T>c;      //默认构造函数,产生一个空的容器
      vector<T>c(c2);  //拷贝构造函数,成为c2的一个拷贝
      vector<T>c=c2;   //拷贝构造函数,成为c2的一个拷贝
      vector<T>c(rv);  //移动构造函数,取rv的内容作为c的内容
      vector<T>c=rv;   //移动构造函数,取rv的内容作为c的内容
      vector<T>c(n);   //通过其中T类型的default构造函数构造一个大小为n的vector
      vector<T>c(n,elem); //建立一个大小为n的容器,每一个内容都是elem
      vector<T>c(begin,end);  //通过[begin,end)内的内容构造一个容器
      vector<T>c(initlist);   //通过初始化列表来初始化容器
      vector<T>c=initlist;    //于上种方法相似
      c.~vector();           //销毁所有元素释放内存

Vector的操作

非更易型操作

c.empty()
c.size()
c.capacity()
c.reserve(num)
c.shrink_to_fit()
c1==c2   //对每个元素执行逻辑判断(即元素要重载逻辑判断)
c1!=c2
c1<c2
c1>c2

赋值

c=c2    //拷贝
c=rv    //以移动赋值的方式赋值到c种
c=initlist
c.assign(n.elem)
c.assign(beg,end)
c.assign(initlist)
c1.swap(c2)
swap(c1,c2)

注:所有赋值操作都有可能调用元素类型的default构造函数,copy构造函数,assignment操作符或析构函数

元素访问

欲访问vector的所有元素,你必须使用 range-base for循环,特定的操作函数或迭代器

注:对于以下操作函数,都是返回元素的reference1

c[idx]  返回下标为idx的元素(不执行范围检查)
c.at(idx)  返回下标为idx的元素(执行范围检查)   越界时抛出out_of_range的异常
c.front()  返回第一元素
c.back()  返回末尾元素

如果对一个空的vector调用operator[],front(),back()会有不明确的行为,因此调用前三者必须要确保访问有效

迭代器相关函数

c.begin()   //返回一个指向首处迭代器
c.cbegin()  //返回一个const迭代器
c.end()    //返回一个指向末尾下一个位置的迭代器
c.cend()   //返回一个指向末尾下一个位置的const迭代器
c.rbegin() //返回一个reverse迭代器,指向末尾的下一个位置
c.rend()   //返回一个reverse迭代器,指向首处的位置
c.crend()   //返回一个const reverse迭代器,指向首处的位置
c.crbegin()  //返回一个const reverse迭代器,指向末尾的下一个位置

注:以下两种情况迭代器会失效、

  1. 在一个较小索引位置上安插或移除元素(在其前方的元素全部失效)

  1. 由于容量变化而引起的内存重新分配

安插和移除元素

c.pop_back()  //移除末尾元素,但不返回它(要保证容器非空)
c.push_back(elem) //附加一个elem的拷贝于末尾
c.insert(pos,elem)  //在pos前的位置插入elem的拷贝,并返回新元素的位置
c.insert(pos,n,elem) //在pos前的位置插入n个elem的拷贝,返回第一个新元素的位置
c.insert(pos,begin,end)  //在pos前的位置插入[beign,end)的拷贝,返回第一个新元素的位置
c.insert(pos,initlist) //在pos前的位置插入initlist的拷贝,返回第一个新元素的位置
c.emplace(pos,args...) //在pos前的位置插入以args为初值构造的元素,返回第一个新元素的位置
c.emplace_back(args...) //附加一个以args为初值的元素构造于末尾
c.erase(pos) //移除pos上的元素,返回下一个元素的位置
c.erase(begin,end) //移除[begin,end)区间上的元素,返回下一个元素的位置
c.resize(num) //将元素数量变为num,如果size()变大,那么则需要内置元素有default构造函数
c.resize(num,elem) //将元素数量变为num,如果size()变大,那么元素则为elem的拷贝
c.clear() //清空容器

关于效能在以下情况下预期安插动作和移除动作会比较快些

  1. 在容器末尾安插或移除元素

  1. 容量一开始就足够大

  1. 一次性安插多个元素相对于多次安插单个元素来的快

由于对于C++11标准并没有提供删除指定值的的元素,以下语句提供了两种删除指定元素的方法

     std::vector<T>coll;
     coll.erase(remove(coll.begin(),coll.end(),val),coll.end);
     std::vector<T>coll;
     auto pos=find(coll.begin(),coll.end(),val);
     if(pos!=coll.end())coll.erase(pos);

注:上面的一种为删除所指定的所有元素,下面这一种为删除指定的第一个元素

将Vector当作C-style Array使用

基于vector内置实现是一个dynamic array,所以在你需要一个动态数组的时候便可以使用vector。例如你可以利用vector存放常用的C字符串(类型为char*或const char*)

      std::vector<char>v;
      v.resize(41);
      strcpy(&v[0],"hello world");
      printf("%s\n",&v[0]);

注:自C++11起,如果你想直接访问vector的元素,不一定要用&a[0],因为成员函数data()也具备同样功能

      std::vector<char,41>v;
      strcpy(v.data(),"hello world");
      printf("%s/n",v.data());

基于C-style Array的特性,在将vector作为其实现时,一定要确保vector大小足够容纳复制进来的数据,例如当作C-string看时,必须会放置一个'\0'元素在vector末端。

注:虽然随机访问迭代器很像pointer,但vector迭代器是由具体实现定义的,也许并不是一个寻常pointer.

      printf("%s\n",v.begin());       //ERROR
      printf("%s\n",v.data());        //OK
      printf("%s\n",&v[0]);           //OK

异常处理

Vector只支持最低限度的逻辑错误检查。Subscript(下标)操作符的安全版本at()是唯一被C++standard认可得以抛出异常的函数

如果vector调用的函数抛出异常C++标准库做出以下保证:

  1. 如果push_back()安插元素时抛出异常,函数将不产生效用

  1. 如果元素的copy/move操作不抛出异常,那么inert(),emplace(),emplace_back(),erase()和push_back()要么成功,要么就不产生效用

  1. pop_back()绝对不会抛出任何的异常

  1. swap()和clear()不抛出异常

注:所有这些保证都基于一个条件:析构函数不得抛出异常

Vector使用实例

下面一个例子展现vector的一个基本用法

#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<iterator>
using namespace std;

int main(){
vector<string>sentence;
sentence.reserve(5);
sentence.push_back("Hello,");
sentence.insert(sentence.end(),{"how","are","you","?"});

copy(sentence.cbegin(),sentence.cend(),ostream_iterator<string>(cout," "));
cout<<endl;

cout<<"max_size():"<<sentence.max_size()<<endl;
cout<<"size():"<<sentence.size()<<endl;
cout<<"capacity():"<<sentence.capacity()<<endl;

swap(sentence[1],sentence[3]);
sentence.insert(find(sentence.begin(),sentence.end(),"?"),"always");
sentence.back()="!";

copy(sentence.cbegin(),sentence.cend(),ostream_iterator<string>(cout," "));
cout<<endl;

cout<<"size():"<<sentence.size()<<endl;
cout<<"capacity():"<<sentence.capacity()<<endl;

sentence.pop_back();
sentence.pop_back();
sentence.shrink_to_fit();

cout<<"size():"<<sentence.size()<<endl;
cout<<"capacity():"<<sentence.capacity()<<endl;
}

输出可能结果如下:

Hello, how are you?
 max_size():1073741823
 size():5
 capacity():5
Hello, you are how always !
 size():6
 capacity():6
 size():4
 capacity():4

参考书籍 《C++标准库》(第二版)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Reol520

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值