C++

9.1.01_vector(向量)

2017-07-03  本文已影响130人  资深小夏

vector(向量)

C++中的一种数据结构,确切的说是一个类。它相当于一个动态的数组,当程序员无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间的目的.

1. 文件包含:

首先在程序开头处加上 #include <vector> 以包含所需要的类文件vector
还有一定要加上 using namespace std;

#include <iostream>
#include <vector>

using namespace std;

2. 变量声明:

// 一维数组
vector <int> a;
// 二维
vector <int*> b;
// 三维
vector <int**> c;

3. 具体的用法以及函数调用:

3.1函数列表

方法名 | 描述
-- |
push_back | 在数组的最后添加一个数据
pop_back | 去掉数组的最后一个数据
at | 得到编号位置的数据
begin | 返回一个迭代器iterator,它指向容器的第一个元素
end | 返回一个迭代器iterator,它指向容器的最后一个元素的下一个位置
front | 得到数组头的引用
back | 得到数组的最后一个单元的引用
max_size | 得到vector最大可以是多大
capacity | 当前vector分配的大小
size | 当前使用数据的大小
resize | 改变当前使用数据的大小,如果它比当前使用的大,填充默认值
reserve | 改变当前vecotr所分配空间的大小
erase | 删除指针指向的数据项
clear | 清空当前的vector
rbegin | 返回一个逆序迭代器reverse_iterator,它指向容器的最后一个元素
rend | 返回一个逆序迭代器reverse_iterator,它指向容器c的第一个元素前面的位置
empty | 判断vector是否为空
swap | 与另一个vector交换数据

3.2 参考代码

    int b = 5;
    a.push_back(b);
    a.push_back(8);
    a.push_back(10);
    a.push_back(22);
    cout << "a's capacity : " << a.capacity() << endl;
    cout << "a[0] : " << a[0] << endl;
    cout << "--------------------------------" << endl << endl;
p_vec(vector <int> *vec)
{
    cout << "vec's capacity : " << vec->capacity() << endl;
    cout << "vec's size : " << vec->size() << endl;
    
    //方法一 
    vector<int>::iterator iter = vec->begin();  
    while (iter != vec->end())  
    {
        cout << *iter << "\t";
        iter++;
    }
    
//    //方法二 
//  for(vector<int>::iterator iter=vec->begin(); iter!=vec->end(); iter++)
//  {
//      cout << *iter ;
//  }

    cout << endl << endl; 
}
    dump_vec(&a);
    
    cout << "a.push_back(2)" << endl;
    a.push_back(2);
    dump_vec(&a);
    
    cout << "a.pop_back()" << endl;
    a.pop_back();
    dump_vec(&a);
    
    cout << "a.at(0): " << a.at(0) << endl; 
    cout << "a.back(): " << a.back() << endl; 
    cout << "a.front(): " << a.front() << endl; 
    cout << endl;
    
    cout << "a.max_size(): " << a.max_size() << endl; 
    cout << "a.capacity(): " << a.capacity() << endl; 
    cout << "a.size(): " << a.size() << endl; 
    cout << endl;

    cout << "a.resize(2)" << endl;
    a.resize(2);
    dump_vec(&a);
    
    cout << "a.resize(4)" << endl;
    a.resize(4);
    dump_vec(&a);
    
    cout << "a.resize(9)" << endl;
    a.resize(9);
    dump_vec(&a);
    
    cout << "a.reserve(6)" << endl;
    a.reserve(6);
    dump_vec(&a);
    
    cout << "a.reserve(10)" << endl;
    a.reserve(10);
    dump_vec(&a);
    
    //a.insert(pos,elem);  在pos位置插入一个elem拷贝
    cout << "a.insert(a.begin() + 4, 9)" << endl;
    //不能在这里取值,因为vector的capacity增加会重新分配内存 
    vector<int>::iterator pos = a.begin() + 3;
    for(int i=0; i<3; i++)
    {   
        pos = a.begin() + 3;
        a.insert(pos + i, 9);
    }
    dump_vec(&a);
    
    //a.erase(beg,end)-删除[beg,end)区间的数据 
    cout << "a.erase(pos)" << endl;
    a.erase(pos);
    dump_vec(&a);
    
    cout << "a.erase(pos, pos+2)" << endl;
    a.erase(pos, pos+2);
    dump_vec(&a);
    
    //a.rbegin() 返回一个逆序迭代器,它指向容器c的最后一个元素
    //a.rend() 返回一个逆序迭代器,它指向容器c的第一个元素前面的位置
    cout << "a.rbegan() & a.rend()" << endl;
    for(vector<int>::reverse_iterator r_iter=a.rbegin(); r_iter!=a.rend(); r_iter++)
    {
        cout << *r_iter << "\t";
    }
    cout << endl;
    dump_vec(&a);
    
    //a.swap(b) 与另一个vector交换数据
    vector <int> m;
    m.push_back(1);
    m.push_back(2);
    m.swap(a);
    dump_vec(&a);
    
    //a.empty() 判断vector是否为空
    cout << "a.empty() : " << a.empty() << endl;
    cout << endl;
    
    //a.clear() 清空当前的vector
    cout << "a.clear()" << endl;
    a.clear();
    cout << "a.empty() : " << a.empty() << endl;

4. 内存管理与效率

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

关于STL(Standard Template Library 标准模板库)容器,最令人称赞的特性之一就是是只要不超过它们的最大大小,它们就可以自动增长到足以容纳你放进去的数据。(要知道这个最大值,只要调用名叫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提供了所有这些函数

这个简介表示了只要有元素需要插入而且容器的容量不足时就会发生重新分配(包括它们维护的原始内存分配和回收,对象的拷贝和析构和迭代器、指针和引用的失效)。所以,避免重新分配的关键是使用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约等于2^10。)

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

vector<int> v;
v.reserve(1000);
for (int i = 1; i <= 1000; ++i)
{
  v.push_back(i);
}

这在循环中不会发生重新分配

在大小和容量之间的关系让我们可以预言什么时候插入将引起vector或string执行重新分配,而且,可以预言什么时候插入会使指向容器中的迭代器、指针和引用失效。例如,给出这段代码,

string s;
...
if (s.size() < s.capacity()) 
{
  s.push_back('x');
}

push_back的调用不会使指向这个string中的迭代器、指针或引用失效,因为string的容量保证大于它的大小。如果不是执行push_back,代码在string的任意位置进行一个insert,我们仍然可以保证在插入期间没有发生重新分配,但是,与伴随string插入时迭代器失效的一般规则一致,所有从插入位置到string结尾的迭代器/指针/引用将失效。

回到本条款的主旨,通常有两情况使用reserve来避免不必要的重新分配。

4.2 使用“交换技巧”来修整vector过剩空间/内存

有一种方法来把它从曾经最大的容量减少到它现在需要的容量。这样减少容量的方法常常被称为“收缩到合适(shrink to fit)”。该方法只需一条语句:vector<int>(ivec).swap(ivec);

表达式vector<int>(ivec)建立一个临时vector,它是ivec的一份拷贝:vector的拷贝构造函数做了这个工作。但是,vector的拷贝构造函数只分配拷贝的元素需要的内存,所以这个临时vector没有多余的容量。然后我们让临时vector和ivec交换数据,这时我们完成了,ivec只有临时变量的修整过的容量,而这个临时变量则持有了曾经在ivec中的没用到的过剩容量。在这里(这个语句结尾),临时vector被销毁,因此释放了以前ivec使用的内存,收缩到合适。

4.3 用swap方法强行释放STL Vector所占内存

template < class T> void ClearVector( vector<T>& v )
{ 
    vector<T> vtTemp;
    vtTemp.swap( v );
} 

如:

vector<int> v ;
v.push_back(1);
v.push_back(3);
v.push_back(2);
v.push_back(4);
vector<int>().swap(v);

//
v.swap(vector<int>()); 

//加大括号{ }是让tmp退出{ }时自动析构
{ std::vector<int> tmp = v;   v.swap(tmp);   }; 

5. Vector 内存管理成员函数的行为测试

C++ STL的vector使用非常广泛,但是对其内存的管理模型一直有多种猜测,下面用实例代码测试来了解其内存管理方式,测试代码如下:

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

int main()
{
    vector<int> iVec;
    cout << "容器 大小为: " << iVec.size() << endl;
    cout << "容器 容量为: " << iVec.capacity() << endl; //0个元素, 容器容量为0

    iVec.push_back(1);
    cout << "容器 大小为: " << iVec.size() << endl;
    cout << "容器 容量为: " << iVec.capacity() << endl; //1个元素, 容器容量为1

    iVec.push_back(2);
    cout << "容器 大小为: " << iVec.size() << endl;
    cout << "容器 容量为: " << iVec.capacity() << endl; //2个元素, 容器容量为2

    iVec.push_back(3);
    cout << "容器 大小为: " << iVec.size() << endl;
    cout << "容器 容量为: " << iVec.capacity() << endl; //3个元素, 容器容量为4

    iVec.push_back(4);
    cout << "容器 大小为: " << iVec.size() << endl;
    cout << "容器 容量为: " << iVec.capacity() << endl; //4个元素, 容器容量为4

    iVec.push_back(5);
    cout << "容器 大小为: " << iVec.size() << endl;
    cout << "容器 容量为: " << iVec.capacity() << endl; //5个元素, 容器容量为8

    iVec.push_back(6);
    cout << "容器 大小为: " << iVec.size() << endl;
    cout << "容器 容量为: " << iVec.capacity() << endl; //6个元素, 容器容量为8

    iVec.push_back(7);
    cout << "容器 大小为: " << iVec.size() << endl;
    cout << "容器 容量为: " << iVec.capacity() << endl; //7个元素, 容器容量为8

    iVec.push_back(8);
    cout << "容器 大小为: " << iVec.size() << endl;
    cout << "容器 容量为: " << iVec.capacity() << endl; //8个元素, 容器容量为8

    iVec.push_back(9);
    cout << "容器 大小为: " << iVec.size() << endl;
    cout << "容器 容量为: " << iVec.capacity() << endl; //9个元素, 容器容量为16
    /* vs2005/8 容量增长不是翻倍的,1.5 如 
       9个元素   容量9 
       10个元素 容量13
    */

    /* 测试effective stl中的特殊的交换 swap() */
    cout << "当前vector 的大小为: " << iVec.size() << endl;
    cout << "当前vector 的容量为: " << iVec.capacity() << endl;
    vector<int>(iVec).swap(iVec);

    cout << "临时的vector<int>对象 的大小为: " 
        << (vector<int>(iVec)).size() << endl;
    cout << "临时的vector<int>对象 的容量为: " 
        << (vector<int>(iVec)).capacity() << endl;
    cout << "交换后,当前vector 的大小为: " << iVec.size() << endl;
    cout << "交换后,当前vector 的容量为: " << iVec.capacity() << endl;

    return 0;
}

6. vector的其他成员函数

7. 备注:在用vector的过程中的一些问题,特此列出讨论:

  1. 此时若对b另外赋值时不会影响a[0]的值
vector <int > a;
int b = 5;
a.push_back(b);

往a向量中压入的是b的值,即a[0]=b,此时a[0]和b是存储在两个不同的地址中的.因此改变b的值不会影响a[0]。

  1. 此时输出的值并不是一开始b数组初始化的值,而是一些无法预计的值.
vector <int*> a;
int *b;
b = new int[4];
b[0]=0;
b[1]=1;
b[2]=2;
a.push_back(b);
delete b;  //释放b的地址空间
for(int i=0 ; i <3 ; i++)
{
   cout<<a[0][i]<<endl;
}

因为是把一个地址(指针)压入向量a,即a[0]=b,因此释放了b的地址也就释放了a[0]的地址,因此a[0]数组中存放的数值也就不得而知了.

上一篇 下一篇

猜你喜欢

热点阅读