在 C++ 的标准模板库(STL)中,vector 容器是一个极为常用且功能强大的数据结构,常被视作单端数组。与普通数组不同,数组占据的是静态空间,一旦声明,其大小便固定不变;而 vector 则具备动态扩展的特性,能够根据实际需求灵活调整自身容量。
所谓动态扩展,并非简单地在原空间之后直接衔接新空间,毕竟无法确保后续空间是否已被占用。实际过程是,vector 会寻觅一块更大的内存空间,将原有的数据完整拷贝至新空间,随后释放掉原来的空间。这种机制使得 vector 在使用上更加灵活,避免了数组可能出现的空间不足问题。
从 vector 容器的结构来看,它提供了丰富的操作接口。比如,push_back () 方法用于在容器尾部插入数据,pop_back () 则用于在尾部删除数据,这便是高效的尾插与尾删操作。front () 代表容器中的第一个元素,back () 代表容器中的最后一个元素,方便我们快速访问特定位置的元素。此外,vector 拥有 4 个重要的迭代器:v.begin () 指向第一个元素的位置,v.end () 指向最后一个元素的下一个位置,v.rbegin () 指向尾部第一个元素的位置,v.rend () 指向第一个元素的前一个位置。通过这些迭代器,我们能够便捷地遍历容器中的元素。同时,vector 还提供了 insert 用于插入数据、erase 用于删除数据等众多实用接口,并且其迭代器支持随机访问,大大提升了操作的灵活性和效率。
具体vector容器的结构图如下所示:
在这里插入图片描述

1. vector 的构造函数

1.vector 提供了多种构造方式,以满足不同的使用场景:
2.vector v:这是默认构造函数,采用模板实现类,创建一个空的 vector。
3.vector(v.begin(), v.end()):区间构造函数,将 v [begin (), end ()) 区间中的元素拷贝给当前 vector。
4.vector(n, elem):构造函数将 n 个 elem 元素拷贝给当前 vector,实现快速初始化。
5.vector(const vector& vec):拷贝构造函数,用于复制一个已有的 vector。

#include<iostream>
#include<vector>
using namespace std;

// 打印vector容器中的元素
void printVector(vector<int>&v) {
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}

int main() {
    vector<int> v1; // 默认构造 无参构造
    for (int i = 0; i < 10; i++) {
        v1.push_back(i);
    }
    printVector(v1);

    // 通过区间方式进行构造
    // v1.begin()代表第一个元素的位置 v1.end()代表最后一个元素的下一个位置
    vector<int> v2(v1.begin() + 1, v1.end() - 1);
    printVector(v2);

    // n个elem方式构造
    vector<int> v3(10, 100); // 10个100的初始化操作
    printVector(v3);

    // 拷贝构造
    vector<int> v4(v3);
    printVector(v4);
    return 0;
}

2. vector 的赋值操作

1.vector 的赋值操作主要包含 operator = 和 assign 两种方式:
vector& operator=(const vector & vec):重载等号操作符,实现将一个 vector 的内容赋值给另一个 vector。
2.assign(beg, end):将 [beg, end) 区间中的数据拷贝赋值给当前 vector,beg 和 end 均为迭代器。
3.assign(n, elem):将 n 个 elem 拷贝赋值给当前 vector。

#include<iostream>
#include<vector>
using namespace std;

// 打印vector容器中的元素
void printVector(vector<int>&v) {
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}

int main() {
    vector<int> v1;
    for (int i = 0; i < 10; i++) {
        v1.push_back(i);
    }
    printVector(v1);

    // 赋值 operator=
    vector<int> v2;
    v2 = v1;
    printVector(v2); // 作用和拷贝构造函数类似 即vector<int> v2(v1)

    // assign赋值
    vector<int> v3;
    v3.assign(v1.begin(), v1.end());
    printVector(v3);

    // n个elem方式赋值
    vector<int> v4;
    v4.assign(10, 100);
    printVector(v4);
    return 0;
}

vector 的赋值方式简洁明了,使用 operator = 或者 assign 均可

3. vector 容量和大小

对于 vector 容器容量和大小的操作,主要借助 empty、capacity、size、resize 等函数:
1.empty():判断容器是否为空,若为空返回 true,否则返回 false。
2.capacity():返回容器当前的容量,即容器在不重新分配内存的情况下能够容纳的元素数量。
3.size():返回容器中实际元素的个数。
4.resize(int num):重新指定容器的长度为 num。若容器变长,则以默认值 0 填充新位置;若容器变短,则末尾超出容器长度的元素将被删除。
5.resize(int num, int elem):重新指定容器的长度为 num。若容器变长,则以 elem 填充新位置;若容器变短,则末尾超出容器长度的元素将被删除

#include<iostream>
#include<vector>
using namespace std;

// 打印vector容器中的元素
void printVector(vector<int> &v) {
    for (auto it = v.begin(); it != v.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}

int main() {
    vector<int> v1;
    for (int i = 0; i < 10; i++) {
        v1.push_back(i);
    }
    printVector(v1);

    // 判断容器是否为空
    if (v1.empty()) { // 为真代表容器为空
        cout << "v1容器为空" << endl;
    }
    else {
        cout << "v1容器不为空" << endl;
        cout << "v1的容量为:" << v1.capacity() << endl;
        cout << "v1中元素的个数为:" << v1.size() << endl;
    }

    // 重新制定大小
    v1.resize(15); // 如果重新指定的大小比原来更长 默认用0填充新的位置
    printVector(v1);
    v1.resize(20, 100); // 利用重载的版本resize可以指定默认填充值
    printVector(v1);
    v1.resize(5); // 如果重新指定的大小比原来短了 超出本分会自动删除
    printVector(v1);
    return 0;
}

通过这些函数,我们能够精准地管理 vector 的容量和大小,以适应不同的业务需求。

4. vector 容器的插入与删除

vector 提供了丰富的插入与删除操作函数:
1.push_back(ele):在尾部插入元素 ele。
2.pop_back():删除最后一个元素。
3.insert(const_iterator pos, ele):在迭代器指向的位置 pos 插入元素 ele。
4.insert(const_iterator pos, int count, ele):在迭代器指向的位置 pos 插入 count 个元素 ele。
5.erase(const_iterator pos):删除迭代器指向的元素。
6.erase(const_iterator start, const_iterator end):删除迭代器从 start 到 end 之间的元素。
7.clear():删除容器中所有元素。

#include<iostream>
#include<vector>
using namespace std;

// 打印vector容器中的元素
void printVector(vector<int> &v) {
    for (auto it = v.begin(); it != v.end(); it++) {
        cout << *it << " ";
    }
    cout << endl;
}

int main() {
    vector<int> v1;
    // 尾插
    v1.push_back(10);
    v1.push_back(20);
    v1.push_back(30);
    v1.push_back(40);
    v1.push_back(50);
    // 遍历
    printVector(v1);

    // 尾删
    v1.pop_back();
    printVector(v1);

    // 插入 第一个参数是迭代器
    v1.insert(v1.begin(), 100);
    printVector(v1);

    v1.insert(v1.end(), 2, 1000);
    printVector(v1);

    // 删除 参数也是迭代器
    v1.erase(v1.begin());
    printVector(v1);

    // 区间删除 仅仅保留最后一个元素
    v1.erase(v1.begin(), v1.end() - 1);
    printVector(v1);

    // 清空
    v1.clear(); // 相当于v1.erase(v1.begin(),v1.end());
    return 0;
}

5. vector 数据存取

存取 vector 中的数据主要有以下几种方式:
在这里插入图片描述

#include<iostream>
#include<vector>
using namespace std;

// vector容器数据存取
int main() {
    vector<int> v1;
    for (int i = 0; i < 10; i++) {
        v1.push_back(i);
    }

    // 利用[]来访问数组中的元素
    for (int i = 0; i < v1.size(); i++) {
        cout << v1[i] << " "; // string的遍历每个字符也可以使用[]
    }
    cout << endl;

    for (int i = 0; i < 10; i++) {
        cout << v1.at(i) << " "; // string的遍历每个字符也可以使用at函数
    }
    cout << endl;

    // 获取容器的第一个元素
    cout << "v1容器中的第一个元素是:" << v1.front();

    // 获取容器中的最后一个元素
    cout << "v1的最后一个元素为:" << v1.back();
    return 0;
}

思考与总结

vector 容器作为 C++ STL 中极为重要的数据结构,宛如一把瑞士军刀,为开发者在处理数据存储与操作时提供了多样化且强大的功能。其设计理念融合了数组的高效访问特性与动态内存管理的灵活性,在不同的编程场景中都展现出卓越的适用性。
从动态扩展机制来看,这一特性无疑是 vector 相较于普通数组的巨大优势。在实际项目中,数据量的变化往往难以预估。以开发一个日志记录系统为例,起初可能只需记录少量关键信息,但随着系统规模扩大与功能增加,日志数据量会呈指数级增长。此时,vector 的动态扩展能力便能大显身手,它无需开发者手动繁琐地管理内存的重新分配与数据迁移,即可自动适应数据量的变化,确保程序稳定运行,极大地提升了开发效率与程序的健壮性。然而,我们也应清醒地认识到,动态扩展并非毫无代价。每次重新分配内存并拷贝数据,都会带来一定的时间与空间开销。尤其在对性能要求极为严苛的场景下,如高频交易系统中的数据处理模块,频繁的动态扩展可能会导致响应延迟,影响系统整体性能。因此,在使用 vector 时,开发者需结合实际业务场景,合理预估数据量的变化范围,尽可能提前预留足够的容量,以减少动态扩展的次数。
vector 丰富的构造函数与赋值操作,为初始化和数据赋值提供了极大的便利。在开发一个图形渲染引擎时,需要创建多个包含顶点坐标的向量容器。若使用默认构造函数,可先创建空的 vector,再根据读取的图形数据逐步插入顶点坐标;而当需要复制已有的顶点数据时,拷贝构造函数或 operator= 赋值操作则能快速完成数据的迁移。在大规模数据处理场景中,若要将一个庞大的 vector 中的部分数据提取出来构建新的容器,区间构造函数与 assign (beg, end) 方法便成为了高效的选择。通过精确指定数据区间,能够避免不必要的数据拷贝,显著提升程序运行效率。这要求开发者对项目中的数据流动与处理逻辑有清晰的把握,根据不同阶段的数据需求,精准选择合适的构造与赋值方式。
容量和大小管理相关函数赋予了开发者对 vector 内存使用情况的精细掌控能力。在开发嵌入式系统软件时,由于设备内存资源极为有限,合理利用 vector 的 empty ()、capacity () 和 size () 函数,能够实时监测内存使用状态,避免内存浪费与溢出。例如,在处理传感器采集的数据时,通过定期检查 vector 的大小与容量,可在数据量达到一定阈值时,及时进行数据处理与清理,释放不再使用的内存空间,确保系统稳定运行。resize () 函数在调整容器大小时,提供了灵活的填充方式,这在数据预处理阶段尤为有用。当需要对输入数据进行规范化处理,统一数据长度时,resize () 函数可轻松实现对容器长度的调整,并根据需求填充默认值或指定元素,为后续的数据处理流程奠定良好基础。
插入与删除操作是 vector 灵活性的重要体现。在实现一个实时任务调度系统时,新任务可能随时加入,而完成的任务需要及时移除。push_back () 和 pop_back () 函数提供了高效的尾插与尾删操作,适用于大多数任务按顺序进出的场景。但当需要在任务队列的特定位置插入或删除任务时,insert () 和 erase () 函数则发挥关键作用。不过,在频繁进行插入和删除操作时,尤其是在容器中间位置操作,会导致元素的大量移动,严重影响性能。因此,在设计任务调度算法时,需综合考虑任务的优先级、到达时间等因素,尽量减少对中间位置元素的操作,或者选择更适合频繁插入删除操作的数据结构,如链表。
数据存取方式的多样性满足了不同场景下对数据访问的需求。在开发科学计算程序时,需要频繁访问 vector 中的元素进行复杂的数值运算。此时,[] 操作符由于其简洁高效的特性,成为了快速访问元素的首选,能够极大地提升计算效率。然而,在一些对数据安全性要求极高的场景,如金融交易系统中的数据校验模块,使用 at () 函数进行边界检查,可有效避免因数组越界访问导致的数据错误与系统崩溃,确保交易数据的准确性与完整性。front () 和 back () 函数则为快速获取容器首尾元素提供了便捷途径,在处理具有特定顺序的数据时,如先进先出(FIFO)或后进先出(LIFO)的数据结构模拟,发挥着重要作用。
vector 的互换容器操作看似简单,却蕴含着巧妙的应用技巧。在内存优化方面,它提供了一种有效的收缩内存空间的方法。以开发大型游戏为例,游戏运行过程中会产生大量临时数据,存储在 vector 中。当这些临时数据不再使用时,虽然可以通过 resize () 函数调整容器大小,但原有的大容量内存空间并不会立即释放。此时,巧用 swap () 函数,能够将 vector 的容量收缩至实际大小,释放多余的内存,为游戏运行的其他环节腾出宝贵的内存资源,提升游戏的整体性能与稳定性。在数据结构转换与算法实现中,互换容器操作也可用于实现一些复杂的数据处理逻辑,如将一个无序的 vector 通过与有序的辅助 vector 互换,实现数据的快速排序与整理。
vector 容器是 C++ 编程中不可或缺的核心工具。它的每一个特性与操作接口都紧密围绕实际编程需求设计,为开发者提供了强大的支持。然而,要充分发挥 vector 的优势,开发者不仅需要深入理解其内部机制与各种操作的性能特点,更要将这些知识与项目的具体业务场景紧密结合。在实际编码过程中,通过不断地实践与优化,根据数据规模、操作频率、性能要求等多方面因素,精心选择最合适的 vector 使用方式,才能编写出高效、健壮且优雅的 C++ 程序。vector 的学习与应用过程,也是开发者提升自身编程素养与解决实际问题能力的过程,值得每一位 C++ 开发者投入时间与精力去深入探索。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐