【C++】vector的介绍和使用(上)

简介: 【C++】vector的介绍和使用(上)

👉vector 的介绍👈

de04e7f621a543e5ac347f73d65091a2.png

vector是表示可变大小数组的序列容器。

就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素 进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自 动处理。

本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小 为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。

vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存 储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。

因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增 长。

与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末 尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好。


使用STL的三个境界:能用,明理,能扩展 ,那么下面学习vector,我们也是按照这个方法去学习。


👉vector 的使用👈


vector学习时一定要学会查看文档:vector的文档介绍,vector在实际中非常的重要,在实际中我们熟悉常见的接口就可以,下面列出了哪些接口是要重点掌握的。


vector 的构造函数和遍历


d9a8846b2d91494189d3d5bdb83541c7.png

1675831579034.png

vector的构造和遍历代码演示


void vectorTest1()
{
    vector<int> first;                                // empty vector of ints
    vector<int> second(4, 100);                       // four ints with value 100
    vector<int> third(second.begin(), second.end());  // iterating through second
    vector<int> fourth(third);                       // a copy of third
    // 用数组来初始化
    // the iterator constructor can also be used to construct from arrays:
    int myints[] = { 16,2,77,29 };
    vector<int> fifth(myints, myints + sizeof(myints) / sizeof(int));
    // []+下标遍历,只有string和vector支持这种方式
    for (size_t i = 0; i < fifth.size(); ++i)
    {
        cout << fifth[i] << ' ';
    }
    cout << endl;
    // 迭代器遍历
    vector<int>::iterator it = fifth.begin();
    while (it != fifth.end())
    {
        cout << *it << ' ';
            ++it;
    }
    cout << endl;
    // 范围for遍历,底层为迭代器
    for (auto e : fifth)
    {
        cout << e << ' ';
    }
    cout << endl;
}


dcd3b65942474761a6dc2863a7566454.png


vector iterator


1675831611193.png

b28da30f6b7f4d44afd60d0fbb8d9ece.png


为什么vector<char> strstring str无法取代对方


网络上发送一段字符串,字符串需要以'\0'结束,而 string 无法被发送。

string 支持比较字符串大小,比较常见;而 vector 的比较大小不常见,也没有什么意义。

函数接口不完全相同。如 string 类有 +=,而 vector 没有 +=。


vector 空间增长问题


1675831642737.png


reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。


resize在开空间的同时还会进行初始化,影响size


reserve 函数接口不会使 vector 缩容,缩容一般是异地缩容,缩容比较大。

969f50c0775c411cb577cceb103a2c07.png

ba566ca262ce408096b5b55083954170.png

resize 的函数接口也能说明内置类型需要有构造函数。


88d3a42192a64a56a9833f20fbc3e99e.png


注:在程序里看到的空间都是虚拟的进程空间,进程会提高页表映射和物理内存建立映射。只有在内核层通过页表才能找到物理地址。


注:reserve 只会修改capacity,不会影响size。

cca1c115217a4096bec8f6c41ba50b46.png


为什么上面的程序会报错呢?因为 reserve 函数将 capacity改成了10,而 size 还是0,所以就会越界访问,触发了 [ ]运算符重载的 assert 报错。


我们可以将上面的代码,改成下面代码的样子,就不会出现这个问题了。


void vectorTest3()
{
    vector<int> v;
    v.reserve(10);
    // 或者将v.reserve(10)换成v.resize(10)
    for (size_t i = 0; i < 10; ++i)
    {
        //v[i] = i;
        v.push_back(i);
    }
}


测试 vector 的默认扩容机制


void TestVectorExpand()
{
    size_t sz;
    vector<int> v;
    sz = v.capacity();
    cout << "making v grow:\n";
    for (int i = 0; i < 100; ++i)
    {
        v.push_back(i);
        if (sz != v.capacity())
        {
            sz = v.capacity();
            cout << "capacity changed: " << sz << '\n';
        }
    }
}


VS的运行结果


428e0ba2c12d4a198473d3ec2331fd18.png


Linux的运行结果


64f6e0c9fb5042599906476fdc1154e0.pngcapacity的代码在VS和g++下分别运行会发现,VS下capacity是按1.5倍增长的,g++是按2倍增长的。这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的。VS是PJ版本STL,g++是SGI版本STL。


为什么扩容多数扩2倍


  • 2 倍比较合适
  • 每次扩少了,会导致频繁扩容
  • 每次扩多了,用不完,存在空间浪费

如果已经确定vector中要存储元素大概个数,可以提前将空间设置足够,就可以避免边插入边扩容导致效率低下的问题了。


void TestVectorExpandOP()
{
    vector<int> v;
    size_t sz = v.capacity();
    v.reserve(100); // 提前将容量设置好,可以避免一遍插入一遍扩容
    cout << "making bar grow:\n";
    for (int i = 0; i < 100; ++i)
    {
        v.push_back(i);
        if (sz != v.capacity())
        {
            sz = v.capacity();
            cout << "capacity changed: " << sz << '\n';
        }
    }
}

99f5237a4b3a4b209a42f0639b9c9e3b.png

d483bccedd2045599f1103db49e70025.png


shift_to_fit缩容


01c675f414474cc7a1d4ee50e554d8bd.png


释放空间由于内存管理的一些原因,不允许分段释放空间,只能整体释放,一般为异地缩容。

👉vector 动态二维数组的理解👈


LeetCode 杨辉三角


给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。

在「杨辉三角」中,每个数是它左上方和右上方的数的和。


3164499cf7554da4ba8e939f20fff8f1.png













相关文章
|
29天前
|
存储 C++ 容器
【C++】vector的底层剖析以及模拟实现
【C++】vector的底层剖析以及模拟实现
|
1月前
|
存储 算法 测试技术
C++:Vector的使用
C++:Vector的使用
|
1月前
|
存储 算法 C++
【C/C++ Vector容量调整】理解C++ Vector:Reserve与Resize的区别与应用
【C/C++ Vector容量调整】理解C++ Vector:Reserve与Resize的区别与应用
52 1
|
1月前
|
存储 C语言 C++
C++初阶--自我实现vector
C++初阶--自我实现vector
|
1月前
|
程序员 C++ 索引
在C++语言中Vector的命名空间的作用
在C++语言中Vector的命名空间的作用
15 0
|
1月前
|
C++ 容器
vector容器-插入和删除c++的讲解要
vector容器-插入和删除c++的讲解要
17 1
vector容器-插入和删除c++的讲解要
|
1月前
|
存储 C++ 容器
c++vector容器-赋直操作讲解
c++vector容器-赋直操作讲解
34 0
|
1月前
|
C++ 容器
vector容器-插入和删除c++
vector容器-插入和删除c++
25 0
|
1月前
|
存储 C++ 容器
vector容器-容量和大N小c++的讲解
vector容器-容量和大N小c++的讲解
18 1
|
2天前
|
存储 缓存 编译器
【C++进阶(五)】STL大法--list模拟实现以及list和vector的对比
【C++进阶(五)】STL大法--list模拟实现以及list和vector的对比