C++ vector resize和reserve

首先必须弄清楚两个概念:
1.capacity
指容器在分配新的存储空间之前能存储的元素总数,也可以说是预分配存储空间的大小。
2. size
指当前容器所存储的元素个数

resize:(resize既分配了空间,也创建了对象)

  • 1、resize(n)
    调整容器的长度大小,使其能容纳n个元素。
    如果n小于容器的当前的size,则删除多出来的元素。否则,添加采用值初始化的元素。
  • 2、 resize(n,t) 多一个参数t,将所有新添加的元素初始化为t。

在C++的容器中,定义

vector<int> v;
v.resize(3);    /*reserve表示容器预留空间,但并不是真正的创建对象,需要通过insert()或push_back()等创建对象,默认对象为0*/
for (int i = 0; i < v.size(); i++)
    v[i] = i;
v.resize(5)     /*resize容器v的大小为5,前三个初始化的值不变,0/1/2,后面的默认为0*/
v.size()        /*v的size为5*/
v.push_back(6)  /*在v的最后面添加一个6*/
v.size()    /* v的size为6,v的值分别是0/1/2/0/0/6 */

reserve:(仅仅分配空间,不建立对象)

reserve()的用法只有一种
reserve(n)
预分配n个元素的存储空间。

直观上的比较:

#include "stdafx.h"
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v;
    v.resize(3);
    cout << v.empty() << endl; //0,即非空有对象
    vector<int> t;
    t.reserve(3);
    cout << t.empty() << endl; //1,空,即没有创建对象,仅分配内存

    vector<int> a;

    a.reserve(100);
    a.resize(50);
    cout << a.size() << "  " << a.capacity() << endl;
    a.resize(150);
    cout << a.size() << "  " << a.capacity() << endl;
    a.reserve(50);
    cout << a.size() << "  " << a.capacity() << endl;
    a.resize(50);
    cout << a.size() << "  " << a.capacity() << endl;
    return 0;

结果为:

0
1
50  100
150  150
150  150
50  150
请按任意键继续. . .

resize()函数和容器的size息息相关。调用resize(n)后,容器的size即为n。
至于是否影响capacity,取决于调整后的容器的size是否大于capacity。

reserve()函数和容器的capacity息息相关。
调用reserve(n)后,若容器的capacity< n,则重新分配内存空间,从而使得capacity等于n。
如果capacity>=n呢?capacity无变化。

从两个函数的用途可以发现,容器调用resize()函数后,所有的空间都已经初始化了,所以可以直接访问。
而reserve()函数预分配出的空间没有被初始化,所以不可访问。

reserve表示容器预留空间,但并不是真正的创建对象,需要通过insert()或push_back()等创建对象。
resize既分配了空间,也创建了对象。

reserve只修改capacity大小,不修改size大小,resize既修改capacity大小,也修改size大小。

vector给我们提供了很多的方便,但是偶尔也会有陷阱。

#include "stdafx.h"
#include<iostream>
#include<vector>
using namespace std;
int main()
{
    vector<int> v;
    //v.resize(4);
    v.push_back(6);

    std::vector<int>::iterator iter1 = v.begin();
    v.push_back(1);
    int n = *(iter1+4);
    printf("v.begin(): %d\n", *v.begin());
    cout << "n: "<<n << endl;
    cout << "v.size(): "<<v.size()<<"\tv.capacity: "<<v.capacity() << endl;
    v.erase(iter1);
    cout << "v.size(): " << v.size() << "\tv.capacity: " << v.capacity() << endl;
    return 0;

}

上面的代码运行时崩溃,就是因为迭代器iter1在vector push_back新值后失效了。切记!
但是为什么会失效?
如果我先预先resize足够大,那么会如何呢?

#include "stdafx.h"
#include<iostream>
#include<vector>
using namespace std;
int main()
{
    vector<int> v;
    //v.resize(4);
    v.push_back(6);

    std::vector<int>::iterator iter1 = v.begin();
    v.push_back(1);
    int n = *(iter1+4);
    printf("v.begin(): %d\n", *v.begin());
    cout << "n: "<<n << endl;
    cout << "v.size(): "<<v.size()<<"\tv.capacity: "<<v.capacity() << endl;
    v.erase(iter1);
    cout << "v.size(): " << v.size() << "\tv.capacity: " << v.capacity() << endl;
    v.clear();
    cout << "v.size(): " << v.size() << "\tv.capacity: " << v.capacity() << endl;
    return 0;

}

上面的代码正常运行,因为之前resize了足够的空间,即迭代器不会失效。
我们就已给vector push_back两个元素为例。resize(1), resize(2), resize(3)程序还是会崩溃。还是容量不足,导致vector重新分配内存,而导致迭代器失效。

如果我们用reserve会是什么样的结果呢

#include "stdafx.h"
#include<iostream>
#include<vector>
using namespace std;
int main()
{
    vector<int> v;
    //v.resize(4);
    v.reserve(4);
    v.push_back(6);

    std::vector<int>::iterator iter1 = v.begin();
    v.push_back(1);
    int n = *(iter1+4);
    printf("v.begin(): %d\n", *v.begin());
    cout << "n: "<<n << endl;
    cout << "v.size(): "<<v.size()<<"\tv.capacity: "<<v.capacity() << endl;
    v.erase(iter1);
    cout << "v.size(): " << v.size() << "\tv.capacity: " << v.capacity() << endl;
    return 0;

}

结果还是会导致崩溃,还是造成迭代器的失效。
接下来的问题就是vector的内存机制,当容量不够时,会申请多少内存?

#include<iostream>
#include<vector>
using namespace std;
int main()
{
    vector<int> v;
    for (int i = 1; i < 100; i++)
    {
        cout << "capacity:" << v.capacity() << ", " << "size" << v.size() << endl;
        v.push_back(i);

    }
    return 0;

}
//输出:
capacity:0, size0
capacity:1, size1
capacity:2, size2
capacity:3, size3
capacity:4, size4
capacity:6, size5
capacity:6, size6
capacity:9, size7
capacity:9, size8
capacity:9, size9
capacity:13, size10
capacity:13, size11
capacity:13, size12
capacity:13, size13
capacity:19, size14
capacity:19, size15
capacity:19, size16
capacity:19, size17
capacity:19, size18
capacity:19, size19
capacity:28, size20
capacity:28, size21
capacity:28, size22
capacity:28, size23
capacity:28, size24
capacity:28, size25
capacity:28, size26
capacity:28, size27
capacity:28, size28
capacity:42, size29
capacity:42, size30
capacity:42, size31
capacity:42, size32
capacity:42, size33
capacity:42, size34
capacity:42, size35
capacity:42, size36
capacity:42, size37
capacity:42, size38
capacity:42, size39
capacity:42, size40
capacity:42, size41
capacity:42, size42
capacity:63, size43
capacity:63, size44
capacity:63, size45
capacity:63, size46
capacity:63, size47
capacity:63, size48
capacity:63, size49
capacity:63, size50
capacity:63, size51
capacity:63, size52
capacity:63, size53
capacity:63, size54
capacity:63, size55
capacity:63, size56
capacity:63, size57
capacity:63, size58
capacity:63, size59
capacity:63, size60
capacity:63, size61
capacity:63, size62
capacity:63, size63
capacity:94, size64
capacity:94, size65
capacity:94, size66
capacity:94, size67
capacity:94, size68
capacity:94, size69
capacity:94, size70
capacity:94, size71
capacity:94, size72
capacity:94, size73
capacity:94, size74
capacity:94, size75
capacity:94, size76
capacity:94, size77
capacity:94, size78
capacity:94, size79
capacity:94, size80
capacity:94, size81
capacity:94, size82
capacity:94, size83
capacity:94, size84
capacity:94, size85
capacity:94, size86
capacity:94, size87
capacity:94, size88
capacity:94, size89
capacity:94, size90
capacity:94, size91
capacity:94, size92
capacity:94, size93
capacity:94, size94
capacity:141, size95
capacity:141, size96
capacity:141, size97
capacity:141, size98

选几个数据分析一下:
0-1-2-3-4-6-9-13-19-28-42-63-94-141

从第二项开始:
2/2+2=3
3/2+3=4
4/2+4=6
6/2+6=9
9/2+9=13
13/2+13=19
19/2+19=28
28/2+28=42
42/2+42=63
63/2+63=94
94/2+94=141

每次扩容50%
删除容器中数据的时候,缓冲区大小并不会改变,仅仅只是清楚了其中的数据,只有在析构函数调用的时候vector才会自动释放缓冲区。也就是说capacity给出就不会释放,而size是已分配的对象所占空间的大小,释放了size也就减小了。

上面其实就是迭代器失效问题。

迭代器失效,有两个层面的意思:

1. 无法通过迭代器++,--操作遍历整个stl容器。记作: 第一层失效。
2. 无法通过迭代器存取迭代器所指向的内存。  记作: 第二层失效。

vector是个连续内存存储的容器,如果vector容器的中间某个元素被删除或从中间插入一个元素, 有可能导致内存空间不够用而重新分配一块大的内存。这个动作将导致先前获取的迭代器,,第一层和第二层均失效。

而造成失效的两个函数:

  • i = insert(i, value)
  • i = erase(i)

造成失效的原因是因为内存的重新分配, 保留下来的迭代器不再指向容器中原来的元素

执行这两个操作时内存分配的具体情况:

  • 1.erase
#include<vector>
#include<iostream>
using namespace std;
int main(){
    vector<int>q{1,2,3,4,5,6,7,8,9,10};
    int cnt = 0;
    int flag = 0;
    for(vector<int>::iterator i = q.begin(); i != q.end(); ++i){
        ++cnt;
        if(cnt > 15){
            cout<<"gg"<<endl;
            break;
        }
        if(*i == 3)              //删除第三个
           i = q.erase(i);
        cout << *i << endl;
        cout << &(*i) << endl;
    }
    return 0;
}
//output
1
03566748
2
0356674C
4
03566750
5
03566754
6
03566758
7
0356675C
8
03566760
9
03566764
10
03566768
请按任意键继续. . .

输出结果分析:

当删除第3个元素以后我们发现第四个元素是紧邻第二个元素的(刚好差一个int的内存)

也就是说vector执行erase(i)后会将迭代器i之后的元素逐个向前移动一个type单位

这也就是i及i之后所有迭代器失效的原因。
比如下面代码:

#include "stdafx.h"
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v;
    vector<int> ::iterator i;
    v.push_back(2);
    v.push_back(4);
    v.push_back(6);
    cout << v[0] << " " << v[1] <<" "<<v[2]<< endl;
    i = v.begin();
    i = v.erase(i);
    cout << *i << endl; //vector中位于i后的元素自动向前移动
    cout << v[0] << " " << v[1] << endl;
    cout << v.end() - v.begin() << endl;
    cout << "capacity(): " << v.capacity() << endl; //容量不变
    cout << "size()" << v.size() << endl;   //对象大小减小
    v.push_back(4);
    cout << "capacity(): "<<v.capacity() << endl;
    cout << "size()"<<v.size() << endl;
    cout << v.end() - v.begin() << endl;
    return 0;
}

即在erase操作后,没有将变量it指向修改后的迭代器,就继续循环,出现asserion failed。解决办法是将变量指向修改后的迭代器。

在vs2015中有这个函数:

iterator erase(const_iterator _Where)
        {   // erase element at where
        if (_VICONT(_Where) != &this->_Get_data()
            || _VIPTR(_Where) < this->_Myfirst()
            || this->_Mylast() <= _VIPTR(_Where))
            _DEBUG_ERROR("vector erase iterator outside range");
        _Move_unchecked(_VIPTR(_Where) + 1, this->_Mylast(), _VIPTR(_Where));
        _Destroy(this->_Mylast() - 1, this->_Mylast());
        _Orphan_range(_VIPTR(_Where), this->_Mylast());
        --this->_Mylast();
        return (_Make_iter(_Where));
        }


iterator _Make_iter(const_iterator _Where) const
        {   // make iterator from const_iterator
        return (iterator(_Where._Ptr, &this->_Get_data()));
        }

函数的返回值是一个迭代器,指向删除元素下一个元素。

  • 2.insert操作(内存不足时)
#include<vector>
#include<iostream>
using namespace std;
int main(){

    vector<int>q{1,2,3,4,5,6,7,8,9,10};                                 //  c++11列表初始化
    vector<int>::iterator j = q.begin();
    j++;
    cout<<"第二个元素:"<<*j<<endl;
    cout<<"第二个元素地址:"<<&(*j)<<endl;
    cout<<"初始vector分配的容量:"<<q.capacity()<<endl;                  //  有多少元素即分配多少内存
    int cnt = 0;
    int flag = 0;  //flag保证只插入一次


    for(vector<int>::iterator i = q.begin(); i != q.end(); ++i){
        ++cnt;
        if(cnt > 15){
            cout<< "gg" <<endl;
            break;
        }
        if(*i == 3&&!flag){
            flag = 1;
            i = q.insert(i,22);
            cout<<"\n插入后第二个元素:"<<*j<<endl;
            cout<<"插入后第二个元素地址:"<<&(*j)<<endl;
            cout<<"插入元素后vector分配的容量:" <<q.capacity() <<endl;
        }
        cout << *i << endl;
        cout << &(*i) << endl;
    }
    return 0;
}
//
output
第二个元素:2
第二个元素地址:0x35e2c
初始vector分配的容量:10
1
0x35e28
2
0x35e2c

插入后第二个元素:198032
插入后第二个元素地址:0x35e2c
插入元素后vector分配的容量:20
22
0x35e60
3
0x35e64
4
0x35e68
5
0x35e6c
6
0x35e70
7
0x35e74
8
0x35e78
9
0x35e7c
10
0x35e80

Process returned 0 (0x0)   execution time : 0.438 s
Press any key to continue.

vector内存分配策略为 二倍扩容(取决于编译器,vs是50%,vscode是2倍) , 每次当内存不够的情况下vector会将容量扩展为当前的两倍.

那这些新分配的会在原内存的后面吗? 根据输出结果显然不是的。

上例代码在插入元素22 后, 新的3号元素内存位置距离上一个元素不是4byte(1个int单位), 也就是说

当vector扩容时, 会在另一个内存分配一段新的内存(原内存的二倍). 并把原内存中的元素全部拷贝到新内存中(如果内存不够)
如果没理解,看下面:

#include "stdafx.h"
#include<vector>
#include<iostream>
using namespace std;
int main()
{

    vector<int> v{ 1,2,3,4,5,6 };
    v.resize(6);
    vector<int>::iterator j = v.begin();
    vector<int>::iterator i = v.begin();
    cout << v.size() << " " << v.capacity() << endl;
    cout << *i << endl;
    cout << "&(*i): "<<&(*i) << endl;
    cout << *(i + 1) << endl;
    cout <<"&(*(i + 1))  "<< &(*(i + 1)) << endl;
    v.insert(i, 22);
    cout << v.size() << " " << v.capacity() << endl;
    cout << "hello" << endl;
    i = v.begin();
    cout << *i << endl;
    cout << "&(*i): " << &(*i) << endl;
    cout << *(i + 1) << endl;
    cout << "&(*(i + 1))  " << &(*(i + 1)) << endl;
    //cout << &(*j) << endl;
    return 0;
}

output
6 6
1
&(*i): 00B21138
2
&(*(i + 1))  00B2113C
7 9
hello
22
&(*i): 00B16100
1
&(*(i + 1))  00B16104
请按任意键继续. . .

即重新分配了内存。

  • 3.insert操作(内存充足时)
#include<vector>
#include<iostream>
using namespace std;
int main(){

    vector<int>q{1,2,3,4,5,6,7,8,9,10};                             
    q.push_back(11);
    cout<<"初始vector分配的容量:"<<q.capacity()<<endl;                  
    int cnt = 0;
    int flag = 0;  //flag保证只插入一次


    for(vector<int>::iterator i = q.begin(); i != q.end(); ++i){
        ++cnt;
        if(cnt > 15){
            cout<< "gg" <<endl;
            break;
        }
        if(*i == 3&&!flag){
            flag = 1;
            i = q.insert(i,22);
            cout<<"插入元素后vector分配的容量:" <<q.capacity() <<endl;
        }
        cout << *i << endl;
        cout << &(*i) << endl;
    }
    return 0;
}
//
output
初始vector分配的容量:20
1
0x2755e58
2
0x2755e5c
插入元素后vector分配的容量:20
22
0x2755e60
3
0x2755e64
4
0x2755e68
5
0x2755e6c
6
0x2755e70
7
0x2755e74
8
0x2755e78
9
0x2755e7c
10
0x2755e80
11
0x2755e84

Process returned 0 (0x0)   execution time : 0.316 s
Press any key to continue.

很显然当内存充足的情况下, 执行insert操作只会将迭代器i及i之后的的所有元素向后移动一个type单位.所以这种情况下即使没有使用返回值也不会发生迭代器失效

#include "stdafx.h"
#include<vector>
#include<iostream>
using namespace std;
int main()
{

    vector<int> v{ 1,2,3,4,5,6 };
    v.resize(8);
    vector<int>::iterator j = v.begin();
    vector<int>::iterator i = v.begin();
    cout << v.size() << " " << v.capacity() << endl;
    cout << *i << endl;
    cout << "&(*i): "<<&(*i) << endl;
    cout << *(i + 1) << endl;
    cout <<"&(*(i + 1))  "<< &(*(i + 1)) << endl;
    v.insert(i, 22);
    cout << v.size() << " " << v.capacity() << endl;
    cout << "hello" << endl;
    i = v.begin();
    cout << *i << endl;
    cout << "&(*i): " << &(*i) << endl;
    cout << *(i + 1) << endl;
    cout << "&(*(i + 1))  " << &(*(i + 1)) << endl;
    //cout << &(*j) << endl;
    return 0;
}
//output
8 9
1
&(*i): 00A85DE0
2
&(*(i + 1))  00A85DE4
9 9
hello
22
&(*i): 00A85DE0
1
&(*(i + 1))  00A85DE4
请按任意键继续. . .

猜你喜欢

转载自blog.csdn.net/qq_30366449/article/details/78396441
今日推荐