【C++ STL】Vector

1、结构

  vector模塑出一个动态数组,因此,它本身是“将元素置于动态数组中加以管理”的一个抽象概念。vector将其元素复制到内部的dynamic array中。元素之间总存在某种顺序,所以vector是一种有序群集。vector支持随机存取,因此只要知道位置,可以在常数时间内存取任何一个元素。根据vector的结构可知,在末端添加或者删除元素,性能相当好,如果在前端或者中部安插或删除元素,性能就不怎么样了,因为操作点之后的元素都需要移到另一位置,而每一次移动都需要调用assignement(赋值)操作符。

2、大小(size)和容量(capacity)

2.1 性能

  vector的优异性能的秘诀之一就是配置比其容纳元素所需更多的内存。vector用于操作大小的函数有size(),empty(),max_size(),另一个有关大小的函数是capacity(),它返回的是vector实际能够容纳的元素数量,如果超越这个数量,vector就有必要重新配置内部存储器。

  vector的容量之所以很重要,有以下两个原因:

  • 一旦内存重新分配,和vector相关的所有元素都会失效,如:references,pointers,iterators。
  • 内存分配很耗时间

2.2 内存分配

2.2.1 reserve

  如果程序管理了和vector元素相关的reference,pointers,iterator,或者执行效率至关重要,那么就必须考虑容器的容量问题。可以使用reserve()来保留适当容量,避免一再的分配内存,只要保留的容量有剩余,就不用担心reference失效。

std::vector<T> v;
v.reserve(n);   //为vector分配容量为n的内存

  还有一点要记住的是,如果调用reserve()所给的参数比当前的vector容量还小,不会发生任何反应,vector不能通过reverse来缩减容量。如何达到时间和空间的最佳效率,由系统版本决定,很多时候为了防止内存破碎,即使不调用reserve,当你第一次安插元素的时候,也会一次性配置整块内存(例如2K)。vector的容量不会缩减,即使删除元素,其reference,pointers,iterators也不会失效,继续指向发生动作之前的位置,然而安插元素可能使这些元素失效(因为安插可能重新分配内存空间)。

2.2.2 构造分配

  另外一种避免重新分配内存的方法是,初始化期间就像构造函数传递附加参数,构造出足够的空间。如果参数是个数值,它将成为vector的起始大小。

std::vector<T> v(500);  //分配能容纳500个T元素的内存大小

  当然,要获得这种能力,这种元素必须提供一个default构造函数,如果类型很复杂,提供了default构造函数,初始化也会很耗时,如果只是为了保留足够的内存,建议使用reverse()。

2.2.3 内存缩减

  当然,要想缩减vector内存还是有方法的,那就是两个vector交换了内容后,两者的容量也会交换,即保留元素,又缩减了内存。

template<class T>
void shrinkCapacity(std::vector<T> &v)
{
    std::vector<T> temp(v);  //初始化一个新的vector
    v.swap(temp);             //交换元素内容
}

  或者通过下列语句:

std::vector<T> (v).swap(v);

  不过应该注意的是,swap()之后,原先所有的reference,pointers,iterator都换了指涉对象,他们仍然指向原本位置,也就是说它们在交换之后,都失效了。

void mvTestSize()
    {
        // size 为20的vector
        vector<int> veciSize1;
        for (int i = 0; i < 20; i++)
        {
            veciSize1.push_back(i);
        }

        cout << "v1 size:" << veciSize1.size() << endl;
        cout << "v1 max size:" << veciSize1.max_size() << endl;
        cout << "v1 capacity:" << veciSize1.capacity() << endl;

        // size为200的vector
        vector<int> veciSize2;
        for (int i = 0; i < 200; i++)
        {
            veciSize2.push_back(i);
        }

        cout << "v2 size:" << veciSize2.size() << endl;
        cout << "v2 max size:" << veciSize2.max_size() << endl;
        cout << "v2 capacity:" << veciSize2.capacity() << endl;

        //分配固定的size
        vector<int> veciSize3;
        veciSize3.reserve(1000);
        for (int i = 0; i < 20; i++)
        {
            veciSize3.push_back(i);
        }

        cout << "v3 size:" << veciSize3.size() << endl;
        cout << "v3 max size:" << veciSize3.max_size() << endl;
        cout << "v3 capacity:" << veciSize3.capacity() << endl;
    }

  输出结果:

3、 vector操作函数

3.1 构造、拷贝和析构

  可以在构造时提供元素,也可以不提供。如果指定大小,系统会调用元素的default构造函数一一制造新元素。


操作


效果


vector<Elem> c


产生一个空的vector,没有任何元素


vector<Elem> c1(c2)


产生另一同一类型的副本 (所有元素都被拷贝)


vector<Elem> c(n)


利用元素的default构造函数生成一个大小为n的vector


vector<Elem> c(n,elem)


产生一个大小为n的vector,每个元素都是elem


vector<Elem> c(beg,end)


产生一个vector,以区间[beg,end)作为初始值


c.~vector<Elem>()


销毁所有元素,释放内存

3.2 非变动操作


操作


效果


c.size()


返回当前的元素数量


c.empty()


判断大小是否为零,等同于0 == size(),效率更高


c.max_size()


返回能容纳的元素最大数量


capacity()


返回重新分配空间前所能容纳的最大元素数量


reserve()


如果容量不足,扩大


c1 == c2


判断c1是否等于c2


c1 != c2


判断c1是否不等于c2(等同于!(c1==c2))


c1 < c2


判断c1是否小于c2


c1 > c2


判断c1是否大于c2


c1 <= c2


判断c1是否小于等于c2(等同于!(c2<c1))


c1 >= c2


判断c1是否大于等于c2 (等同于!(c1<c2))

3.3 赋值操作


操作


效果


c1 = c2


将c2的元素全部给c1


c.assign(n,elem)


复制n个elem给c


c.assign(beg,end)


将区间[beg,end)赋值给c


c1.swap(c2)


将c1和c2元素互换


swap(c1,c2)


将c1和c2元素互换,全局函数

  值得注意的是,所有的赋值操作都可能调用元素的default构造函数,copy构造函数和assignment操作符或者析构函数。

3.4 元素存取


操作


效果


c.at(idx)


返回索引idx标示的元素,如果越界,抛出越界异常


c[idx]


返回索引idx标示的元素,不进行范围检查


c.front()


返回第一个元素,不检查第一个元素是否存在


c.back()


返回最后一个元素,不检查最后一个元素是否存在

  按照c/c++的惯例,第一个元素的索引为0,所以n个元素的索引为n-1。只有at()会检查范围,其他函数不检查范围,如果越界,除了at()会抛出异常,其他函数会引发未定义行为,所以使用索引的时候,必须确定索引有效,对一个空的vector使用front()和back()都会发生未定义行为,所以调用他们,必须保证vector不为空。

  对于non-const-vector,这些函数返回的都是reference,可以通过这些函数来改变元素内容。

3.5 迭代器相关函数

  vector提供了一些常规函数来获取迭代器。vector的迭代器是random access iterators(随机存取迭代器),简单的来说就是数组一样的迭代器,可以直接使用下标操作。


操作


效果


c.begin()


返回一个随机存取迭代器,指向第一个元素


c.end()


返回一个随机存取迭代器,指向最后一个元素的下一个位置


c.rbegin()


返回一个逆向迭代器,指向逆向迭代的第一个元素


c.rend()


返回一个逆向迭代器,指向逆向迭代的最后一个元素的下一个位置

3.5.1迭代器失效条件

  • 使用者在较小位置安插或移除元素。
  • 由于容量变化而引起内存重新分配。

3.5.2 安插和移除元素

  vector提供安插和移除操作函数,调用这些函数,必须保证参数合法:

  • 迭代器必须指向一个合法位置。
  • 区间的起始位置不能在结束位置之后。
  • 不能从空容器中移除元素。

  关于性能,下列情况的安插和移除操作可能效率高一些:

  • 在容器的尾部安插或删除元素。
  • 容量一开始就很大。
  • 安插多个元素,调用一次比调用多次快。

  安插和删除元素,会使“作用点”之后的各元素的reference、pointers、iterators失效,如果安插操作导致内存重新分配,该容器的所有reference、pointers、iterators都会失效。


操作


效果


c.insert(pos,elem)


在pos位置上插入一个elem副本,并返回新元素位置


c.insert(pos,n,elem)


在pos位置插入n个elem副本,无返回值


c.insert(pos,beg,end)


在pos位置插入[beg,end]内所有元素的副本,无返回值


c.push_back(elem)


在尾部添加一个elme副本


c.pop_back()


移除最后一个元素


c.erase(pos)


移除pos上的位置,返回下一个元素的位置


c.erase(beg,end)


移除[beg,end)内的所有元素,并返回下一元素的位置


c.resize(num)


将元素的位置改为num个(如果size变大了,多出来的元素需要default构造函数来完成构造))


c.resize(num,elem)


将元素的位置改为num个(如果size变大了,多出来的元素都是elem的副本))


c.clear()


移除所有元素,将容器清空

4、代码示例

// cont/vector1.cpp

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

   int main()
   {

       //create empty vector for strings
       vector<string> sentence;

       //reserve memory for five elements to avoid reallocation
       sentence.reserve(5);

       //append some elements
       sentence.push_back("Hello,");
       sentence.push_back("how");
       sentence.push_back("are");
       sentence.push_back("you");
       sentence.push_back("?");

       //print elements separated with spaces
       copy (sentence.begin(), sentence.end(),
             ostream_iterator<string>(cout," "));
       cout << endl;

       //print ‘‘technical data‘‘
       cout << " max_size(): " << sentence.max_size() << endl;
       cout << " size():     " << sentence.size()     << endl;
       cout << " capacity(): " << sentence.capacity() << endl;

       //swap second and fourth element
       swap (sentence[1], sentence [3]);

       //insert element "always" before element "?"
       sentence.insert (find(sentence.begin(),sentence.end(),"?"),
                        "always");

       //assign "!" to the last element
       sentence.back() = "!";

       //print elements separated with spaces
       copy (sentence.begin(), sentence.end(),
             ostream_iterator<string>(cout," "));
       cout << endl;

       //print "technical data" again
       cout << " max_size(): " << sentence.max_size() << endl;
       cout << " size():     " << sentence.size()     << endl;
       cout << " capacity(): " << sentence.capacity() << endl;

   }

输出:

Hello, how are you ?
     max_size(): 268435455
     size():     5
     capacity(): 5
   Hello, you are how always !
     max_size(): 268435455
     size():     6
     capacity(): 10
时间: 2024-10-01 23:45:14

【C++ STL】Vector的相关文章

【STL】- vector的使用

初始化: 1. 默认构造: vector<int> vint; 2. 用包含10个元素的数组初始化: vector<int> vint(ia, ia+10); 算法: 1. vint.push_back(i); 2. vint.size(); 3. vint[i]; 代码: 1 #include <vector> 2 #include <iostream> 3 using namespace std; 4 5 int ia[] = {123,1,32,53,

【STL】vector的insert方法详解

#include<vector> #include<iostream> using namespace std; int main() { vector<int> v(3); v[0]=2; v[1]=7; v[2]=9; v.insert(v.begin(),8);//在最前面插入新元素. v.insert(v.begin()+2,1);//在迭代器中第二个元素前插入新元素 v.insert(v.end(),3);//在向量末尾追加新元素. vector<int

【转】【C++ STL】深入解析神秘的 --- 仿函数

原文:http://blog.csdn.net/tianshuai1111/article/details/7687983 一,概述        仿函数(functor),就是使一个类的使用看上去象一个函数.其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了. 有些功能的的代码,会在不同的成员函数中用到,想复用这些代码. 1)公共的函数,可以,这是一个解决方法,不过函数用到的一些变量,就可能成为公共的全局变量,再说为了复用这么一片代码,就要单立出一个函数

hdoj 1022 Train Problem I 【简易STL】

题意:不解释(这题是学数据结构必做的) 以前自学数据结构的时候,只是会顺序表来模拟栈.最近简单学习了stack头文件 又来做了一遍(还是以前的味道) 代码: #include <stdio.h> #include <stack> #include <string.h> using std::stack; stack<char > s; char s1[100], s2[100]; int vis[10]; char stac[100]; int main()

【源码】Vector、Stack源码解析

注:以下源码基于jdk1.7.0_11 Vector算是一个历史遗留下来的类,现在已基本被ArrayList取代.本文出于学习的目的来分析下这个类. 从图上可以看出Vector和ArrayList同样都直接继承于AbstractList,说明这两者功能上还是很相像的,事实也正是如此. 下面我们依然通过源码的方式解读Vector这个类. public class Vector<E> extends AbstractList<E> implements List<E>, R

【转】【STL】vector详解

转自:http://blog.sina.com.cn/s/blog_9f1c0931010180cy.html Vectors   vector是C++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库.vector之所以被认为是一个容器,是因为它能够像容器一样存放各种类型的对象,简单地说,vector是一个能够存放任意类型的动态数组,能够增加和压缩数据.为了可以使用vector,必须在你的头文件中包含下面的代码:#include <vector> 构造函数.

【STL】vector 用法总结

vector就是一个不定长数组.其中还包括一些常用操作,例如:若a是一个vector,可以用a.size()读取它的大小,a.resize()改变大小,a.push_back()向尾部添加元素,a.pop_back()删除最后一个元素,a.clear()清空所有元素, a.empty()测试是否为空. vector是一个模板类,所以要用vector<int>a 或者vector<double>a 这样的方式来声明.vector<int>是一个类似于int a[]的整数数

【C++/STL】list的实现(采用空间配置器和迭代器)

在list库函数的编译中仍然有很多问题,在源代码的编译中有些内容尚未搞懂,在后期的学习中会进行更加深入的学习,希望大家可以对我的问题提出建议和批评,谢谢大家~ 具体的代码如下: #include <iostream> using namespace std; //采用迭代器和空间配置器所实现的双向链表的基本功能 template<class _Ty,class _A = allocator<_Ty> > //定义模板类 class list //list类 { publ

【技术向】vector容器的用法

部分转自:http://www.cnblogs.com/wang7/archive/2012/04/27/2474138.html 在c++中,vector是一个十分有用的容器,下面对这个容器做一下总结. 1. 初始化 1 vector <Elem> // 创建一个空的vector. 2 vector <Elem> c1(c2) // 复制一个vector 3 vector <Elem> c(n) // 创建一个vector,含有n个数据,数据均已缺省构造产生 4 ve