vector原理、存储、遍历、缩容、迭代器失效问题

vector比较详细的原理及用法

存储数据

void text()
{
    // 存储int类型数据,当然也可以存储其它类型如char、string、类等
    vector<int> vec;
    vec[0] = 10;
    vec[1] = 9;
}
  1. 上面这种存储方式是错误的!vector是数组的确没有错,但是最开始的vec是没有大小的,所以是没有办法用下标这种方式赋值的。

  2. 可以把vec的容量(capacity:数组的大小)和大小(size:数组中元素的个数)打印出来 cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl; 结果是size = 0, capacity = 0

  3. 正确的存储方式如下:

    vector<int> vec;
    cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    
    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(3);
    cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    

    运行结果:

    size = 0, capacity = 0
    size = 3, capacity = 4
    
  4. 可以发现通过调用vec的成员函数push_back()函数存储3个数据后,vec的大小是3,capacity是4。有同学可能当时就蒙了,4哪来的啊?别急,后面会讲解vector的扩容机制的!我们先从最简单的一点一点看。有同学又会说了,我哪知道vec有啥成员函数啊?其实可以通过cppreference网站查看c++的一些函数相关信息,内容很全。传送门:https://en.cppreference.com/w/;英文看不懂没关系,还有中文版https://zh.cppreference.com/w/%E9%A6%96%E9%A1%B5

  5. 其实还是有办法可以用下标的方式给vector数组赋值的

    void test()
    {
        vector<int> vec;
        cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    
        // 设置vec的大小是3,(size=capacity=3)
        vec.resize(3);
        cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    
        // vec有了大小之后,就可以采用下标的方式赋值了
        vec[0] = 1;
        vec[1] = 2;
        cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    }
    
  6. 还可以在创建vec对象时直接初始化

    // 初始化vec的数据
    vector<int> vec = {1, 2, 3};
    
    // 还可以指定vec的大小为5,默认初始值为0
    vector<int> vec1(5);
    
    // 指定vec大小同时并初始化元素为2
    vector<int> vec2(5, 2);
    
    //打印数组元素,下面马上讲解打印数组元素
    for (auto elem : vec2){
            cout << elem << endl;
    }
    

遍历vector数组

遍历方式一(上面刚见过):
vector<int> vec;
cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;

vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;

for (auto elem : vec){      
        cout << elem << endl;
}
  1. for循环中的auto关键字是c++中自动类型推导,当然你将auto换成int一点毛病没有。

  2. 还以以对遍历的值进行修改,如将元素值每个都加一

    for (int &elem : vec){
            cout << elem << endl;
            elem += 1;
    }
    
  3. 注意哦,我这里的for循环可是带了引用&的,所以可以修改值哦。ps:引用就是变量的别名

遍历方式二:常规方式
for (size_t i = 0; i < vec.size(); ++i){
        cout << vec[i] << endl;
}
  1. 注意:这里的判断条件是 i < vec.size()而不是 i < vec.capacity();上面说过了,size代表的是数组元素的个数,capacity代表的是数组的容量大小。就好比你买了一个书架,书架可以放5本书,但你只放了2本书,那么这个5就是书架的容量,2代表当前书架拥有书的数量
  2. 有同学for循环写成for (auto i = 0; i < vec.size(); ++i),会报warning。然后就疑惑了,说auto不是能自动推导类型么?咋就报类型不匹配了呢?因为你把 i 初始化为0,那么自动推导出来的iint类型了。而vec.size()返回的是size_t类型(其实就是一个unsigned long),(对于返回类型可以在cppreference看到的很清楚)
遍历方式三:迭代器方式
for (auto it = vec.begin(); it != vec.end(); ++it)
{
    cout << *it << " ";
}
cout << endl;

vector的存储机制

  1. 先看代码

    vector<int> vec;
    cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    
    vec.push_back(1);
    cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    vec.push_back(2);
    cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    vec.push_back(3);
    cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    vec.push_back(4);
    cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    vec.push_back(5);
    cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    

    运行结果:

    size = 0, capacity = 0
    size = 1, capacity = 1
    size = 2, capacity = 2
    size = 3, capacity = 4
    size = 4, capacity = 4
    size = 5, capacity = 8
    
  2. 基本上这些数据就可发现,size与插入的总元素个数是相同的,插入一个元素,那么size就+1;而capacity的增长是按照之前有数据容量的2倍增长的(windows貌似是1.5倍增长的)。

  3. vector的容量机制是:当数组中的size==capacity之后,若是还需要存储数据,系统会重新申请一块内存空间,空间大小是之前的2倍

  4. 当然也可以使用reserve()函数指定最开始数组容量,假如存储的数据有点多,最开始系统会频繁进行扩容,所以会损耗性能,那么就可以一开始就给数组分配一块空间,若是最开始指定的空间存满了,后续还要存储数据,那么就按照当前容量的2倍进行扩容

    vector<int> vec;
    cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    
    // 预留数组容量大小为10,只有capacity大小是10
    vec.reserve(10);
    cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    
    for (int i = 0; i < 11; ++i)
    {
        vec.push_back(i);
    }
    cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    

    运行结果:

    size = 0, capacity = 0
    size = 0, capacity = 10
    size = 11, capacity = 20
    
  5. 如果你真的认为所有时刻都是2倍扩容,那就错了,使用insert插入数据扩容机制就发生了一些改变!

    vector<int> vec;
    
    vec.push_back(1);
    vec.push_back(2);
    vec.push_back(3);
    for (auto elem : vec)
    {
        cout << elem << " ";
    }
    cout << endl;
    cout << "vector.size = " << vec.size() << ", vec.capacity = " << vec.capacity() << endl;
    
    auto it = vec.begin() + vec.size();
    cout << "insert two datas!, datas num > (capacity - size) && < capacity" << endl;
    vec.insert(it, 2, 7);
    cout << "vector.size = " << vec.size() << ", vec.capacity = " << vec.capacity() << endl;
    
    cout << "insert six datas!, datas num >= capacity" << endl;
    it = vec.begin() + vec.size();
    vec.insert(it, 6, 9);
    cout << "vector.size = " << vec.size() << ", vec.capacity = " << vec.capacity() << endl;
    
    // 运行结果
    1 2 3 
    vector.size = 3, vec.capacity = 4
    insert two datas!, datas num > (capacity - size) && < capacity
    vector.size = 5, vec.capacity = 6
    insert six datas!, datas num >= capacity
    vector.size = 11, vec.capacity = 11
    

    注意观察插入元素与当前数组剩余空间与数组容量之间的关系!不难得出:

    1. 当使用insert插入元素数量大于当前数组剩余空间时,且小于数组总容量时,会按照当前size*2的方式扩容
    2. 当使用insert插入元素数量大于等于容量时,会按照size+当前插入元素的个数扩容!

vector缩容

  1. 如果想要节约空间,而放弃效率的话还可以使用shrink_to_fit();来移除未使用的容量

    vec.shrink_to_fit();
    cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    
  2. 节约空间还可以使用成员函数swap()来交换两个数组的数据

    vector<int> vec;
    cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    
    vec.reserve(10);
    cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    
    for (int i = 0; i < 11; ++i)
    {
        vec.push_back(i);
    }
    cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    
    {
        //在块作用域创建的vec1是临时的变量,当离开作用域自动被销毁
        vector<int> vec1(vec.size());
        vec1.swap(vec);  //副作用:交换后vec数据没了
    }
    
    cout << "size = " << vec.size() << ", capacity = " << vec.capacity() << endl;
    

    运行结果:

    size = 0, capacity = 0
    size = 0, capacity = 10
    size = 11, capacity = 20
    size = 11, capacity = 11
    

vector迭代器失效问题

问题一

在进行insert的时候,由于插入的元素个数不确定,在插入的过程中会进行扩容,而迭代器还指向旧的空间,所以迭代器已经失效。解决方案,每次操作之前,将迭代器重新置位!

vector<int> vec;

vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
auto it = vec.begin();

cout << "vector.size = " << vec.size() << ", vec.capacity = " << vec.capacity() << endl;
for (; it != vec.end(); ++it)
{
    cout << *it << " ";
}
cout << endl;

vec.insert(it, 2, 6);
cout << "insert into 2 datas!" << endl;

cout << "vector.size = " << vec.size() << ", vec.capacity = " << vec.capacity() << endl;

// 迭代器失效,打印脏数据
for (; it != vec.end(); ++it)
{
    cout << *it << " ";
}
cout << endl;

// 正确方式
for (auto it = vec.begin(); it != vec.end(); ++it)
{
    cout << *it << " ";
}
cout << endl;
问题二

在erase的时候,删除一个元素,会导致后面所有的元素都向前移动,此时迭代器会指向待删除元素的后一个元素,此时也是迭代器失效的一种形式,如果此时将迭代器偏移,会导致有些元素删除不干净!

vector<int> vec;

vec.push_back(1);
vec.push_back(2);
vec.push_back(2);
vec.push_back(2);
vec.push_back(5);

for (auto it = vec.begin(); it != vec.end(); ++it)
{
    cout << *it << " ";
}
cout << endl;

for (auto it = vec.begin(); it != vec.end(); ++it)
{
    // 删除数组中元素为3的元素
    if (2 == *it)
    {
        vec.erase(it);
    }
}

for (auto it = vec.begin(); it != vec.end(); ++it)
{
    cout << *it << " ";
}
cout << endl;

运行结果:

1 2 2 2 5 
1 2 5 

可以看到三个2只删除了两个!

如果本文对你有帮助,记得一键三连哦,一键三连笑哈哈,代码能力顶呱呱!

本人能力有限,如有错误,望不吝指正;原创不易,欢迎转载,转载请注明出处!