• 【C++】STL—vector的常用接口


    vector的常用接口

    请添加图片描述

    前言

    这里我们讲解vector的时候就不会像string类一样这么详细,string类讲的详解一些,为后面做铺垫。有了string类的基础,大家看一些接口就知道是什么意思,这里给大家而是讲解一些常用的接口,剩下的接口不太常用,如果大家遇到了,查文档即可,这里推荐两个C++文档,cplusplus.com 以及 cppreference.com。第二个网站是C++的官网,但是感觉不太好用,我平时都喜欢用第一个,感觉较为方便。

    一、vector介绍

    1. vector是表示可变大小数组的序列容器。
    2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
    3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小。为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
    4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
    5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
    6. 与其它动态序列容器相比(deques, lists and forward_lists), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起lists和forward_lists统一的迭代器和引用更好。

    二、vector的使用

    1. vector的定义

    (constructor)构造函数声明接口说明
    vector() 重点无参构造
    vector(size_type n, const value_type& val = value_type())构造并初始化n个val
    vector (const vector& x); (重点)拷贝构造
    vector (InputIterator first, InputIterator last);使用迭代器进行初始化构造

    示例:

    • vector() 无参构造
    int main()
    {
        vector<int> v;
    }
    
    • 1
    • 2
    • 3
    • 4
    • vector(size_type n, const value_type& val = value_type())构造并初始化n个val
    vector<int> v1(10, 5);//用10个5来初始化v1
    
    • 1
    • vector (const vector& x); 拷贝构造
    vector<int> v1(10, 1);//使用10个1来初始化v1
    vector<int> v2(v1);//使用v1去拷贝构造v2
    
    • 1
    • 2
    • vector (InputIterator first, InputIterator last); 使用迭代器进行初始化构造
    vector<int> v1(10, 1);//使用10个1来初始化v1
    vector<int> v3(v1.begin(), v1.end());//使用迭代器拷贝构造v1的数据
    
    • 1
    • 2
    • 还可以通过迭代器初始化来获得string的字符串
    string s = "hello world";
    vector<char> v(s.begin(), s.end());
    
    • 1
    • 2

    2. vector的遍历

    接口名称使用说明
    operator[ ]下标 + [ ]
    迭代器begin + end 或 rbrgin + rend
    范围for底层还是借用迭代器实现

    2.1.operator[ ]

    operator[ ]就是对[ ]的重载,我们可以像C语言那样使用下标 + [ ]去访问元素。

    void test_vector1()
    {
    	vector<int> v;
    	v.push_back(1);
    	v.push_back(2);
    	v.push_back(3);
    	v.push_back(4);
    	
    	for (size_t i = 0; i < v.size(); ++i)
    	{
    		cout << v[i] << "";
    	}
    	cout << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.2.迭代器

    vector的迭代器和string的迭代器近乎一致,规则也都类似。

    iterator的使用接口说明
    begin + endbegin获取第一个数据位置的iterator/const_iterator,end获取最后一个数据的下一个位置的iterator/const_iterator
    rbegin + rendrbegin获取最后一个数据位置的reverse_iterator,rend获取第一个数据前一个位置的reverse_iterator

    image-20221130105612237

    • 正向迭代器:
    void test()
    {
        vector<int>::iterator it = v.begin();
    	while (it != v.end())
    	{
    		cout << *it << " ";
    		it++;
    	}
    	cout << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 反向迭代器:
    void test()
    {
        vector<int>::iterator it = v.rend();
        while(it != v.rbegin())
        {
            cout << *it << " ";
            it++;
    	}
        cout << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.3.范围for

    范围for的底层就是替换了迭代器,先前string类已经实现过。

    void test()
    {
        for(auto e : v)
    	{
    		cout << e << " ";  
    	}
    	cout << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3. vector的空间增长问题

    容量空间接口说明
    size获取数据个数
    capacity获取容量大小
    empty判断是否为空
    resize(重点)改变vector中的size
    reserve(重点)改变vector的capacity
    max_sizevector中的最大数据
    Shrink to fit收缩字符串容量

    3.1.size和capacity

    vector的size是用来获取有效数据个数,而capacity就是获取容量大小:

    void test_vector2()
    {
    	vector<int> v;
    	v.push_back(1);
    	v.push_back(2);
    	v.push_back(3);
    	v.push_back(4);
    	cout << v.capacity() << endl;
    	cout << v.size() << endl;
    
    	v.push_back(5);
    	v.push_back(6);
    	v.reserve(10);
    	cout << v.capacity() << endl;
    	cout << v.size() << endl;
    	
    	// 比当前容量小时,不缩容
    	v.reserve(4);
    	cout << v.capacity() << endl;
    	cout << v.size() << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    3.2.max_size和empty

    max_size的作用是返回vector容器可以容纳的最大元素数,用类型的最大值除以sizeof(类型)即max_size。

    empty是判断vector容器是否为空。

    void test()
    {
    	vector<int> v;
    	cout << v.max_size() << endl;
        v.push_back(1);
        v.push_back(1);
        v.push_back(1);
        if(empty(v))
            cout << "vector is empty" << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.3.reserve

    reserve的作用是请求更改容量capacity。

    1. 如果 n 大于当前容量,则该函数会导致容器重新分配其存储,可能会发生异地扩容,将其容量增加到 n(或更大)。
    2. 在其他情况下,函数调用不会导致重新分配内存,即容量不会受影响。
    void test()
    {
        vector<int> v;
    	v.push_back(1);
    	v.push_back(2);
    	v.push_back(3);
    	v.push_back(4);
    	cout << v.capacity() << endl;
    	cout << v.size() << endl;
        // 比当前容量大时,增容
    	v.reserve(10);
    	cout << v.capacity() << endl;
    	cout << v.size() << endl;
        // 比当前容量小时,不缩容
    	v.reserve(4);
    	cout << v.capacity() << endl;
    	cout << v.size() << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 补充:
    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';
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    测试结果如下:

    image-20221130152659155

    • capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。
    • reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。
    • resize在开空间的同时还会进行初始化,影响size。
    • 为什么一定要按照1.5倍或2倍增长呢?

    答案:合适,单次增容越多,插入N个值,增容次数越少,效率就越高,但是浪费空间就越多。单次增容越少,就会导致频繁增容,效率低下。1.5倍或2倍是最平衡的做法。

    3.4.resize

    resize在空间的同时也进行了初始化。

    1. 如果 n 小于当前容器大小,则内容将减少到其前 n 个元素,删除超出(并销毁)的元素。
    2. 如果 n 大于当前容器大小 ,则通过在末尾插入所需数量的元素以达到 n 的大小来扩展内容。如果指定了 val,则新元素将初始化为 val 的副本,否则,它们将进行值初始化。
    void test()
    {
    	vector<int> v;
    	v.push_back(1);
    	v.push_back(2);
    	v.push_back(3);
    	v.push_back(4);
    	cout << v.capacity() << endl;
    	cout << v.size() << endl;
    	v.resize(8);
    	v.resize(15, 1);
    	v.resize(3);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3.5.Shrink to fit

    image-20221130205414883

    1. 请求容器减小其容量以适应其大小。

    2. 请求是非绑定的,容器实现可以自由优化,并使vector具有大于其大小的容量。

    3. 这可能会导致重新分配,但对vector大小没有影响,并且不能更改其元素。

    void test()
    {
        vector<int> v(100);
        cout << v.capacity() << " "; // 100
        str.resize(10);
        cout << v.capacity() << " "; // 100
        str.shrink_to_fit();
        cout << v.capacity() << " "; // 10
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4. vector的增删查改

    vector增删查改接口说明
    push_back(重点)尾插
    pop_back(重点)尾删
    find查找(注意这个是算法模块实现,不是vector的成员接口)
    insert在下标为pos的前面插入val
    erase删除pos位置的数据
    swap交换两个vector的数据空间
    operator[] (重点)像数组一样访问
    sort排序(注意这里也不是vector的函数接口,只是用于排序)
    assign分配内容给vector,即初始化vector,覆盖原来的内容

    4.1.push_back和pop_back

    这俩接口和string的没啥区别,这里简单给出测试用例:

    void TestBitVector2()
    {
    	vector<int> v;
    	v.push_back(1);
    	v.push_back(2);
    	v.push_back(3);
    	v.push_back(4);
    	v.push_back(5);
    	cout << v.size() << endl;
    	cout << v.capacity() << endl;
    	cout << v.front() << endl;
    	cout << v.back() << endl;
    	cout << v[0] << endl;
    	for (auto e : v)
    	{
    		cout << e << " ";
    	}
    	cout << endl;
    
    	v.pop_back();
    	v.pop_back();
    	for (auto e : v)
    	{
    		cout << e << " ";
    	}
    	cout << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    insert和erase

    insert就是在迭代器下标为pos的前面插入val,erase就是删除迭代器下标为pos的值。

    void test()
    {
        vector<int> v;
    	v.push_back(1);
    	v.push_back(2);
    	v.push_back(3);
    	v.push_back(4);
    	v.insert(v.begin(), 0);
    	for (auto e : v)
    	{
    		cout << e << " ";
    	}
    	cout << endl;
    
    	v.erase(v.begin() + 1);
    	for (auto e : v)
    	{
    		cout << e << " ";
    	}
    	cout << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    4.2.find

    这里的find并不是vector的成员函数,这个是算法模块实现。其本质就是在一段左闭右开的迭代器区间去寻找一个值。找到了就返回它的迭代器,找不到就返回它的开区间那个迭代器。

    image-20221130153309055

    void test()
    {
        vector<int> v1;
    	v1.push_back(10);
    	v1.push_back(20);
    	v1.push_back(30); 
    	v1.insert(v1.begin(), 40);
    	v1.insert(v1.begin()+2, 50);
        // 没有find成员
    	//vector::iterator it = v.find(3);
    	vector<int>::iterator it = find(v1.begin(), v1.end(), 20);
    	if (it != v1.end())
    	{
    		v1.insert(it, 2);
    	}
    	for (auto e : v1)
    	{
    		cout << e << " ";
    	}
    	cout << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    4.3.operator[]

    operator[ ]就是对[ ]的重载,我们可以像C语言那样使用下标 + [ ]去访问元素。

    这里有一个经典错误,我们开辟了10个容量的空间,然后去访问,发现访问不了。这是因为你只开空间但是没有初始化。所以这里我们根本进不去for循环。我们用v[6]去访问元素,发现程序崩溃。这里我们应该用resize。

    void test()
    {
    	vector<int> v;
    	v.reserve(10); // size没有变化
    	//v.resize(10);
    	//v[6];
    	for (size_t i = 0; i < v.size(); i++)
    	{
    		v[i] = i; // assert
    		v.at(i) = i; // 抛异常
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    我们在讲string类的时候说过,operator[]访问异常会断言报错,而at会抛异常,异常我们以后在涉及。

    4.4.sort

    sort函数也不是vector的成员函数,这里只是为了对vector创建的数据进行排序。

    image-20221130153405360

    void test()
    {
    	vector<int> v;
    	v.push_back(1);
    	v.push_back(6);
    	v.push_back(-2);
    	v.push_back(23);
    	v.push_back(-6);
    //默认sort是升序
    	sort(v.begin(), v.end());
    	for (auto e : v)
    		cout << e << " "; 
    	cout << endl;
    //要排降序,就要用到仿函数,具体是啥后续详谈
    	sort(v.begin(), v.end(), greater<int>());
    	for (auto e : v)
    		cout << e << " "; 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    4.5.assign

    给vector重新赋值,可能会引起底层容量改变。也可以给其他容器赋值,比如string。

    void test()
    {
    	vector<int> v;
    	v.push_back(1);
    	v.push_back(2);
    	v.push_back(3);
    	v.push_back(4);
    	v.assign(10, 1); // 赋值为10个1	
    	for (auto e : v)
    	{
    		cout << e << " ";
    	}
    	cout << endl;
    
    	vector<int> v1;
    	v1.push_back(10);
    	v1.push_back(20);
    	v1.push_back(30);
    	v.assign(v1.begin(), v1.end());//以v1的元素赋值v
    	for (auto e : v)
    	{
    		cout << e << " "; // 10 20 30
    	}
    	cout << endl;
    
    	string s("hello world");// 这里去掉首字母h,尾字母d
    	v.assign(++s.begin(), --s.end()); // 因为模板为int类型,所以string类的字符为强转为int
    	for (auto e : v)
    	{
    		cout << e << " ";
    	}
    	cout << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
  • 相关阅读:
    使用位运算技巧实现加减乘除
    天工开物 #8 Async Rust 的实现
    git安装相关
    leetcode经典面试150题---1.合并两个有序数组
    【javaEE】多线程初阶(Part5单例模式)
    cesium在地形上贴地添加各种entity
    QLable 类使用教程
    实训素材纯HTML+CSS代码 (教育主题 3页 )
    c语言使用fdk_aac库对aac音频解码为pcm
    如何优雅的排空节点上的pod?云服务商是如何回收机器的?
  • 原文地址:https://blog.csdn.net/m0_64224788/article/details/128122461