【c++丨STL】vector的使用

简介: 本文介绍了C++ STL中的`vector`容器,包括其基本概念、主要接口及其使用方法。`vector`是一种动态数组,能够根据需要自动调整大小,提供了丰富的操作接口,如增删查改等。文章详细解释了`vector`的构造函数、赋值运算符、容量接口、迭代器接口、元素访问接口以及一些常用的增删操作函数。最后,还展示了如何使用`vector`创建字符串数组,体现了`vector`在实际编程中的灵活性和实用性。

前言

       之前我们学习了string类的使用及模拟实现,相比c语言的字符串,它的功能更强,安全性更高,操作方式更便捷。然而,在处理更复杂的数据集合时,仅仅依赖字符串往往显得力不从心,尤其是当我们需要管理一系列具有相同类型的数据项时,如一系列的数字、字符或甚至是其他字符串。这时,一个更为强大且灵活的数据结构——向量(vector)便应运而生。


       本篇文章,我们将介绍vector并深入探讨其使用方法。


vector相关接口查阅:


https://legacy.cplusplus.com/reference/vector/


vector简要介绍

       vector是STL中的一种容器,它用于表示可变大小的数组,底层使用动态顺序表实现。相比传统的数组,vector附带了一系列操作接口,并且由于内存是动态分配的,所以不必担心插入元素时内存不足的问题。由于vector强大的功能和灵活性,我们在c++编程中经常使用vector来表示内存连续的序列。



       我们使用vector时,要引头文件<vector>,并且该容器定义在命名空间std当中。


一、vector的默认成员函数

       vector显示实现的默认成员函数如下:



构造函数(constructor)


vector一共有6个构造函数,其中最常用的有5个:

image.png

代码示例:

#include <vector>
#include <iostream>
using namespace std;
 
void print(vector<int>& v)
{
    for (int i = 0; i < v.size(); i++)
    {
        cout << v[i] << ' ';
    }
    cout << endl;
}
 
int main()
{
    vector<int> v1;//无参构造,创建一个整形空数组
    vector<int> v2(3, 5);//n个val值构造
    vector<int> v3(v2);//拷贝构造
    vector<int> v4(v3.begin() + 1, v3.end());//迭代器区间构造
    vector<int> v5({ 1,2,3,4,5 });//初始化器构造
 
    print(v1);
    print(v2);
    print(v3);
    print(v4);
    print(v5);
    return 0;
}



这里需要注意:vector的本质是一个类模板,我们在定义时需要配合"< >"标明其元素类型。


析构函数(destructor)


析构函数用于释放动态分配的内存空间,在对象声明周期结束时自动调用。


赋值运算符重载operator=


赋值重载用于完成已经存在的对象的拷贝赋值。这三个赋值重载函数中,第一个和第三个比较常用:

image.png

代码示例:

#include <vector>
#include <iostream>
using namespace std;
 
void print(vector<int>& v)
{
    for (int i = 0; i < v.size(); i++)
    {
        cout << v[i] << ' ';
    }
    cout << endl;
}
 
int main()
{
    vector<int> v1;
    vector<int> v2({ 1,2,3,4,5 });
 
    v1 = v2;//将v2赋值给v1
    print(v1);
 
    v1 = { 5,4,3,2,1 };//初始化器赋值
    print(v1);
    return 0;
}



二、vector的容量接口


vector的容量接口有七个,我们介绍几个比较常用的容量接口:


size和capacity


size用于获取vector中数据元素的个数。注意:元素个数并不等同于空间容量。



capacity用于获取当前为数组分配的空间容量,以元素表示。在任何情况下,size都小于等于capacity。

不同编译器对插入元素时capacity的增长量设置可能不同,vs下基本按照1.5倍增长,而g++下按照2倍增长。


代码示例:

#include <vector>
#include <iostream>
using namespace std;
 
int main()
{
    vector<int> v1;
    vector<int> v2(10, 1);
    vector<int> v3;
    for (int i = 0; i < 10; i++) v3.push_back(1);//插入10个元素
 
    cout << "v1 size:" << v1.size() << " capacity:" << v1.capacity() << endl;
    cout << "v2 size:" << v2.size() << " capacity:" << v2.capacity() << endl;
    cout << "v3 size:" << v3.size() << " capacity:" << v3.capacity() << endl;
    return 0;
}



resize和reserve


resize的作用是改变容器的size。


       如果参数n的值小于当前size,则该函数会将size调整为n值,并且删除超出的元素。


       如果参数n的值大于当前size,则会在末尾插入元素至size等于n值。如果我们传了val参数,则后续插入的元素被初始化为val的副本;如果没有传val参数,则会调用其构造函数来初始化元素(实际上,内置类型也有"构造函数",内置类型默认构造初始化的结果一般为0)。另外,如果参数n的值也大于capacity的值,则容器会额外分配空间便于插入元素。


注意:该函数本质是通过插入或删除元素来改变size的值。



reserve的作用是为容器预留空间(增容)。


       如果参数n小于等于当前容量,则不会进行任何操作。


       如果参数n大于当前容量,则该函数使容器重新分配其存储空间,将其容量增加到n(或更大)。


注意:该函数对容器内的元素不会有任何影响。


代码示例:

#include <vector>
#include <iostream>
using namespace std;
 
int main()
{
    vector<int> v1 = { 1,2,3,4,5 };
    cout << "v1 size:" << v1.size() << " capacity:" << v1.capacity() << endl;
 
    v1.resize(10);
    cout << "v1 size:" << v1.size() << " capacity:" << v1.capacity() << endl;
 
    v1.reserve(20);
    cout << "v1 size:" << v1.size() << " capacity:" << v1.capacity() << endl;
    return 0;
}



empty


empty用于判断容器是否为空(即元素个数是否为0)。如果为空,返回true;否则返回false。


该函数不会修改容器的元素内容。


三、vector的迭代器接口


vector的迭代器和string使用方法类似,我们都可以通过迭代器接口来获取迭代器,进而访问数据元素。



begin和end



begin返回指向首元素的正向迭代器,而end返回指向末尾元素“后一位”的正向迭代器。


代码举例:

#include <vector>
#include <iostream>
using namespace std;
 
int main()
{
    vector<int> v1 = { 1,2,3,4,5 };
    for (auto it = v1.begin(); it != v1.end(); it++)
    {
        cout << *it << ' ';
    }
    cout << endl;
    return 0;
}


rbegin和rend



rebegin返回指向末尾元素的反向迭代器,rend返回指向首元素“前一位”的反向迭代器。


代码示例:

#include <vector>
#include <iostream>
using namespace std;
 
int main()
{
    vector<int> v1 = { 1,2,3,4,5 };
    for (auto it = v1.rbegin(); it != v1.rend(); it++)
    {
        cout << *it << ' ';
    }
    cout << endl;
    return 0;
}


cbegin、cend、crbegin、crend

这四种迭代器是在前四种迭代器的基础上修改为只能进行读操作,不可修改指向的值。这里不再赘述。


四、vector的元素访问接口


元素访问接口便于我们访问和修改容器内的元素。


operator[ ]


operator[ ]可以让我们像访问数组元素一样访问和修改容器中的元素。它的使用方法与下标引用操作符相同。这里需要注意:可移植程序不应该使用超出范围的参数n调用此函数,因为这会导致未定义的行为。


代码举例:

#include <vector>
#include <iostream>
using namespace std;
 
int main()
{
    vector<int> v1 = { 1,2,3,4,5 };
    for (size_t i = 0; i < v1.size(); i++)
    {
        cout << v1[i] << ' ';
    }
    cout << endl;
    return 0;
}


at


at的作用与operator[ ]相同,该函数需要传入的参数对应数组下标。与operator[ ]不同的是:如果越界访问,at会抛出异常,而operator[ ]会导致未定义行为。


代码示例:

#include <vector>
#include <iostream>
using namespace std;
 
int main()
{
    vector<int> v1 = { 1,2,3,4,5 };
    for (size_t i = 0; i < v1.size(); i++)
    {
        cout << v1.at(i) << ' ';
    }
    cout << endl;
    return 0;
}


front和back



front返回容器首元素的引用,而back返回容器末尾元素的引用。我们可以使用这两个函数来访问或修改容器的首尾元素。


代码示例:

#include <vector>
#include <iostream>
using namespace std;
 
int main()
{
    vector<int> v1 = { 1,2,3,4,5 };
    cout << v1.front() << endl;
    cout << v1.back() << endl;
    return 0;
}


五、vector的增删查改


push_back


push_back的功能是尾插一个元素,该元素被初始化为val的副本。当容量不足时会自动扩容。


pop_back


pop_back用于删除尾部元素。


insert


insert用于在指定位置进行插入。这里的位置需要用迭代器进行指定。该函数支持单个元素插入、n个val值插入、迭代器区间插入以及初始化器插入。完成插入操作后,它会返回新插入部分首元素的迭代器。


使用举例:

#include <vector>
#include <iostream>
using namespace std;
 
void print(vector<int>& v)
{
    for (int i = 0; i < v.size(); i++)
    {
        cout << v[i] << ' ';
    }
    cout << endl;
}
 
int main()
{
    vector<int> v1 = { 1,2,3,4,5 };
    print(v1);
 
    v1.insert(v1.end(), 10);//元素插入
    print(v1);
 
    v1.insert(v1.end(), 3, 7);//n个val值插入
    print(v1);
 
    v1.insert(v1.end(), v1.begin(), v1.end());//迭代器区间插入
    print(v1);
 
    v1.insert(v1.end(), { 9,9,9,9,9 });//初始化器插入
    print(v1);
    return 0;
}


erase


erase的作用是删除指定位置的元素或区间。指定的元素或区间都需要用迭代器表示。函数的返回值是删除部分的后一个位置的迭代器。


代码举例:

#include <vector>
#include <iostream>
using namespace std;
 
void print(vector<int>& v)
{
    for (int i = 0; i < v.size(); i++)
    {
        cout << v[i] << ' ';
    }
    cout << endl;
}
 
int main()
{
    vector<int> v1 = { 1,2,3,4,5,6,7,8,9,10 };
    print(v1);
 
    v1.erase(v1.begin() + 3);//删除元素
    print(v1);
 
    v1.erase(v1.begin() + 1, v1.begin() + 4);//删除区间
    print(v1);
    return 0;
}



swap


swap用于交换两个vector容器的内容。


当然,与string相同,它也有一个非成员函数版的swap:



clear


clear的作用是将容器中的所有元素清除,并且将size置为0。


find


在vector的成员函数中,我们发现并没有用于查找的函数(find),那么如何进行查找呢?答案是使用STL实现的通用find。该find函数定义在算法库<algorithm>当中,用于容器元素的查找。它接受两个迭代器参数和一个值参数,表示需要查找的区间和值。如果找到了,函数会返回指向第一个查找到的元素的迭代器,否则返回尾迭代器。


使用举例:

#include <vector>
#include <iostream>
using namespace std;
 
int main()
{
    vector<int> v1 = { 1,2,3,4,5,6,7,8,9,10 };
    int n = 0;
    while (cin >> n)
    {
        if (find(v1.begin(), v1.end(), n) != v1.end())
        {
            cout << "找到了" << endl;
        }
        else
        {
            cout << "没找到" << endl;
        }
    }
    return 0;
}



六、vector灵活运用:创建字符串数组

       之前提到,vector本质是一个类模板,我们可以自由设定容器的元素类型。那么,是否可以用vector创建一个字符串数组呢?


代码示例:

#include <vector>
#include <iostream>
using namespace std;
 
int main()
{
    vector<string> v;
    v.push_back("hello");
    v.push_back("world");
 
    for (auto& e : v)
    {
        cout << e << ' ';
    }
    cout << endl;
}



在上述代码中,我们传入模板参数“string”,这使得vector容器内的每一个元素都是一个string对象,这样就可以用vector来存储多个字符串了。由于c++自动调用自定义成员构造函数和析构函数的特性,我们也无需担心初始化与空间释放问题。当然我们也可以使用" vector<vector<int>> "来创建一个动态的二维数组,运用方式十分灵活。


总结

       今天我们学习了STL另一个容器--vector的使用。不难发现,它的许多接口名称与string是相同的,这种实现方式也有助于我们学习、使用和理解STL各种各样的容器。之后博主会带大家深入学习vector的底层原理,并尝试模拟实现。如果你觉得博主讲的还不错,就请留下一个小小的赞在走哦,感谢大家的支持❤❤❤

相关文章
|
1天前
|
人工智能 自动驾驶 大数据
预告 | 阿里云邀您参加2024中国生成式AI大会上海站,马上报名
大会以“智能跃进 创造无限”为主题,设置主会场峰会、分会场研讨会及展览区,聚焦大模型、AI Infra等热点议题。阿里云智算集群产品解决方案负责人丛培岩将出席并发表《高性能智算集群设计思考与实践》主题演讲。观众报名现已开放。
|
18天前
|
存储 人工智能 弹性计算
阿里云弹性计算_加速计算专场精华概览 | 2024云栖大会回顾
2024年9月19-21日,2024云栖大会在杭州云栖小镇举行,阿里云智能集团资深技术专家、异构计算产品技术负责人王超等多位产品、技术专家,共同带来了题为《AI Infra的前沿技术与应用实践》的专场session。本次专场重点介绍了阿里云AI Infra 产品架构与技术能力,及用户如何使用阿里云灵骏产品进行AI大模型开发、训练和应用。围绕当下大模型训练和推理的技术难点,专家们分享了如何在阿里云上实现稳定、高效、经济的大模型训练,并通过多个客户案例展示了云上大模型训练的显著优势。
|
21天前
|
存储 人工智能 调度
阿里云吴结生:高性能计算持续创新,响应数据+AI时代的多元化负载需求
在数字化转型的大潮中,每家公司都在积极探索如何利用数据驱动业务增长,而AI技术的快速发展更是加速了这一进程。
|
12天前
|
并行计算 前端开发 物联网
全网首发!真·从0到1!万字长文带你入门Qwen2.5-Coder——介绍、体验、本地部署及简单微调
2024年11月12日,阿里云通义大模型团队正式开源通义千问代码模型全系列,包括6款Qwen2.5-Coder模型,每个规模包含Base和Instruct两个版本。其中32B尺寸的旗舰代码模型在多项基准评测中取得开源最佳成绩,成为全球最强开源代码模型,多项关键能力超越GPT-4o。Qwen2.5-Coder具备强大、多样和实用等优点,通过持续训练,结合源代码、文本代码混合数据及合成数据,显著提升了代码生成、推理和修复等核心任务的性能。此外,该模型还支持多种编程语言,并在人类偏好对齐方面表现出色。本文为周周的奇妙编程原创,阿里云社区首发,未经同意不得转载。
|
6天前
|
人工智能 自然语言处理 前端开发
100个降噪蓝牙耳机免费领,用通义灵码从 0 开始打造一个完整APP
打开手机,录制下你完成的代码效果,发布到你的社交媒体,前 100 个@玺哥超Carry、@通义灵码的粉丝,可以免费获得一个降噪蓝牙耳机。
3426 13
|
25天前
|
缓存 监控 Linux
Python 实时获取Linux服务器信息
Python 实时获取Linux服务器信息
|
11天前
|
人工智能 自然语言处理 前端开发
什么?!通义千问也可以在线开发应用了?!
阿里巴巴推出的通义千问,是一个超大规模语言模型,旨在高效处理信息和生成创意内容。它不仅能在创意文案、办公助理、学习助手等领域提供丰富交互体验,还支持定制化解决方案。近日,通义千问推出代码模式,基于Qwen2.5-Coder模型,用户即使不懂编程也能用自然语言生成应用,如个人简历、2048小游戏等。该模式通过预置模板和灵活的自定义选项,极大简化了应用开发过程,助力用户快速实现创意。
|
13天前
|
人工智能 自然语言处理 前端开发
用通义灵码,从 0 开始打造一个完整APP,无需编程经验就可以完成
通义灵码携手科技博主@玺哥超carry 打造全网第一个完整的、面向普通人的自然语言编程教程。完全使用 AI,再配合简单易懂的方法,只要你会打字,就能真正做出一个完整的应用。本教程完全免费,而且为大家准备了 100 个降噪蓝牙耳机,送给前 100 个完成的粉丝。获奖的方式非常简单,只要你跟着教程完成第一课的内容就能获得。
5508 10
|
7天前
|
人工智能 C++ iOS开发
ollama + qwen2.5-coder + VS Code + Continue 实现本地AI 辅助写代码
本文介绍在Apple M4 MacOS环境下搭建Ollama和qwen2.5-coder模型的过程。首先通过官网或Brew安装Ollama,然后下载qwen2.5-coder模型,可通过终端命令`ollama run qwen2.5-coder`启动模型进行测试。最后,在VS Code中安装Continue插件,并配置qwen2.5-coder模型用于代码开发辅助。
573 4
|
10天前
|
云安全 人工智能 自然语言处理