Skip to content

📦11. Vector 向量

数组虽然使用方便,但其长度固定的局限性十分突出——一旦定义,无法在运行时改变大小。vector 是 C++ 标准库提供的动态数组容器,可自动管理存储空间。

0. 模板基础

在学习 vector 之前,须先理解模板的概念,以便正确认识 vector<int><int> 的含义。

0.1 问题:类型重复带来的代码冗余

假设已编写一个 IntArray 类,用于存储整数:

cpp
class IntArray {
    int data[100];   // 最多存储 100 个整数
    int size = 0;
public:
    void add(int val) { data[size++] = val; }
};

若后续需要存储 doublestring 类型,则需编写几乎相同的 DoubleArrayStringArray 等类。

这种重复显然是低效的——代码逻辑完全一致,仅类型不同

0.2 模板:类型的参数化

模板(template) 是一种类型参数化机制——在编写代码时暂不指定具体类型,留待使用时再由编译器确定:

cpp
template <typename T>
class MyArray {
    T data[100];    // T 为类型参数,实例化时替换为具体类型
    int size = 0;
public:
    void add(T val) { data[size++] = val; }
};

这里的 T 是一个类型参数(type parameter),充当具体类型的占位符。使用时:

cpp
MyArray<int>    arr1;   // T = int,编译器生成 int 版本
MyArray<double> arr2;   // T = double,编译器生成 double 版本
MyArray<string> arr3;   // T = string

编译器遇到 MyArray<int> 时,将模板中所有 T 替换为 int,生成一份专门处理 int 类型的代码——此过程称为模板实例化

0.3 直观类比

模板(类模板)         具体的类
                         ┌──────────────┐
MyArray<int>             │   int 版本   │
MyArray<double>    =     │  double 版本 │
MyArray<string>          │  string 版本 │
                         └──────────────┘

一份模板可以生成多个不同具体类型的类。

0.4 函数模板

不仅类可以模板化,函数同样支持模板:

cpp
template <typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

cout << max(3, 5);           // T = int → 返回 5
cout << max(3.14, 2.71);     // T = double → 返回 3.14
cout << max('A', 'B');       // T = char → 返回 'B'

同一份 max 函数模板可适用于整数、浮点数、字符等多种类型,无需为每种类型单独编写。

0.5 模板、类与对象的关系

将三个概念串联起来:

模板(template)  →  类(class)  →  对象(object)
   │                     │               │
   │ 实例化              │ 创建           │ 使用
   ▼                     ▼               ▼
vector<int>           v              v.push_back(10)
概念说明
模板 template<typename T>类型参数化的类定义,T 为类型参数
具体的类 vector<int>将 T 替换为具体类型后生成的类
对象 v根据类创建的实际变量

是面向对象编程的基础,模板则进一步提升了类的通用性——使一个类能够适配任意数据类型,避免重复编码。实际上 string 也是一个模板类(basic_string<char> 的别名),只是 <char> 在别名定义中被隐藏了。

0.6 vector<int> 的语义解读

vector<int>    →  元素类型为 int 的 vector
vector<double> →  元素类型为 double 的 vector

尖括号 <> 内指定容器所要存储的元素类型。


理解模板的基本概念后,接下来正式学习 vector 这一最常用的模板类。

1. 数组的局限性

cpp
int scores[5];           // 固定存储 5 个整数
// 若需存储第 6 个数据,数组容量已不足

实际编程中,数据量往往在程序运行时才能确定。例如:

  • 需要读入 N 个数,但 N 的值由用户输入决定
  • 从文件中读取学生成绩,文件中的学生数量未知
  • 数据需要动态增长,无法预先估算最大容量

传统数组难以应对这类场景——要么分配过多造成空间浪费,要么分配不足导致数据溢出。

vector 正是为解决这一问题而设计。

2. vector 概述

vector 是 C++ 标准库提供的动态数组容器(亦称向量),定义于 <vector> 头文件中。

cpp
#include <vector>

其主要特性如下:

  • 动态扩容:元素数量增长时自动分配更多内存
  • 长度查询:通过 .size() 成员函数获取当前元素个数
  • 下标访问:支持 v[0]v[1] 等下标操作,语法与数组一致
  • 丰富接口:支持尾部添加、尾部删除、插入、清空等操作

vector 本身也是一个(基于模板实现),因此自带了各类成员函数。

3. 定义与初始化

cpp
#include <vector>

vector<int> v1;              // 空的 vector,元素类型为 int
vector<int> v2(5);           // 包含 5 个元素,均初始化为 0
vector<int> v3(5, 3);        // 包含 5 个元素,均初始化为 3 → [3,3,3,3,3]
vector<int> v4 = {1, 2, 3};  // 列表初始化 → [1,2,3](C++11 起支持)

语法格式为 vector<类型>,类型参数置于尖括号 <> 中。

尖括号语法源自模板机制——vector 是一个可适配任意元素类型的容器。vector<double> 表示存储 double 类型的动态数组,vector<string> 表示存储 string 类型的动态数组。

4. 基本操作

4.1 尾部添加 —— push_back()

cpp
vector<int> v;
v.push_back(10);   // v = [10]
v.push_back(20);   // v = [10, 20]
v.push_back(30);   // v = [10, 20, 30]

push_back() 在 vector 尾部追加一个新元素。vector 内部自动管理内存分配,当前容量不足时自动扩容。

4.2 元素访问 —— [].at()

cpp
vector<int> v = {10, 20, 30};
cout << v[0];     // 10,下标操作与数组一致
cout << v[1];     // 20
cout << v[2];     // 30

v[0] = 99;        // 可通过下标修改元素

.at()[] 功能相似,但额外执行越界检查

cpp
cout << v[100];      // 越界访问,行为未定义(可能读到垃圾数据或导致崩溃)
cout << v.at(100);   // 越界访问,抛出异常(安全性更高)

4.3 获取长度 —— .size()

cpp
vector<int> v = {10, 20, 30};
cout << v.size();    // 3

v.push_back(40);
cout << v.size();    // 4

4.4 删除尾部元素 —— .pop_back()

cpp
vector<int> v = {10, 20, 30};
v.pop_back();        // 删除最后一个元素 → v = [10, 20]
v.pop_back();        // → v = [10]

4.5 判空和清空

cpp
vector<int> v = {10, 20, 30};
cout << v.empty();   // false(不为空)

v.clear();           // 清空所有元素
cout << v.empty();   // true
cout << v.size();    // 0

4.6 遍历 vector

方式一:下标遍历

cpp
vector<int> v = {10, 20, 30, 40, 50};
for (int i = 0; i < v.size(); i++) {
    cout << v[i] << " ";
}
// 输出:10 20 30 40 50

方式二:基于范围的 for 循环(C++11)

cpp
for (int x : v) {
    cout << x << " ";
}
// 输出:10 20 30 40 50

基于范围的 for 循环语法更为简洁,无需处理下标索引,直接遍历每个元素。

5. 常用操作速查表

操作写法说明
定义vector<int> v;空 vector
尾部添加v.push_back(x);在末尾加一个元素
尾部删除v.pop_back();删除最后一个元素
访问元素v[i]v.at(i)按下标访问
长度v.size()当前元素个数
判空v.empty()空返回 true
清空v.clear()删除所有元素
排序sort(v.begin(), v.end());需要<algorithm> 头文件

TIP

sort(v.begin(), v.end()) 可以对 vector 排序,默认从小到大。需要 #include <algorithm>

6. vector 与数组对比

对比项传统数组 int a[N]vector
长度固定,编译时确定动态,运行时可变
长度获取需通过 sizeof 计算.size() 直接获取
越界检测无越界检查(危险).at() 执行越界检查
参数传递退化为指针,长度信息丢失传递 vector 本身,长度信息保留
新增元素无法动态增加push_back() 随时添加
性能略优(无额外开销)基本无差异(现代编译器的优化)

除嵌入式系统等内存极度受限的场景外,应优先使用 vector 而非传统数组

7. 例题:统计成绩

题目: 输入 N 个学生的成绩,输出最高分、最低分和平均分。

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

int main() {
    int n;
    cout << "请输入学生人数:";
    cin >> n;

    vector<int> scores;    // 用 vector 存成绩

    cout << "请输入 " << n << " 个成绩:";
    for (int i = 0; i < n; i++) {
        int s;
        cin >> s;
        scores.push_back(s);   // 逐个添加到 vector
    }

    // 计算最高分、最低分、总分
    int max = scores[0];
    int min = scores[0];
    int sum = 0;

    for (int i = 0; i < scores.size(); i++) {
        if (scores[i] > max) max = scores[i];
        if (scores[i] < min) min = scores[i];
        sum += scores[i];
    }

    double avg = (double)sum / scores.size();

    cout << "最高分:" << max << endl;
    cout << "最低分:" << min << endl;
    cout << "平均分:" << avg << endl;

    return 0;
}

如果用传统数组,你得先知道 N 才能定义数组大小——而 vector 可以在不知道 N 的情况下先定义好,边读边加。

8. 和 string 的联系

学完 string 又学 vector,你会发现它们有很多相似之处:

操作stringvector<int>
长度s.length() / s.size()v.size()
下标访问s[i]v[i]
尾部添加s += 'a's.push_back('a')v.push_back(x)
判空s.empty()v.empty()
清空s.clear()v.clear()

这是 C++ 标准库的设计理念之一:不同类型的容器,操作接口尽量保持一致,这样学起来事半功倍。

后面在 L2 的 STL 章节,你还会学到更多的容器(如 listmap),操作方式都非常相似!

9. 总结

  • vector 是 C++ 的动态数组,长度可以随数据量自动变化
  • push_back() 在尾部添加,用 [].at() 访问
  • .size() 获取当前元素个数
  • 可以用下标或范围 for 循环遍历
  • 优先用 vector,而不是传统数组——除非你有非常特殊的理由

💬 与我联系 QQ:774165314 | 微信:ArinRiver