STL-vector

STL---<vector>

介绍
  • vector是STL容器中常用的容器,和数组类似,由于其大小size可变,常用于数组大小不可知的情况下来替代数组;
  • vector是为了实现动态数组而产生的容器,在取名的时候取了向量这个名字,但是我觉得还是一个数组;
  • vector也是一种容器,在 内存中连续排列 ,因此可以通过下标快速访问,时间复杂度为1。然而,连续排列也意味着大小固定,数据超过vector的预定值时vector将自动扩容。
创建和使用方法
创建方法

vector本质是类模板,可以存储任何类型的数据。数组在声明前需要加上数据类型,而vector则通过模板参量设定类型。

c++ 复制代码
#include <iostream>
#include <vector> // 使用vector必须包含的头文件
using namespace std; // 使用标准命名空间,避免每次都要写std::

// 定义一个打印vector内容的函数,方便演示
void printVector(const vector<int>& vec) {
    for (size_t i = 0; i < vec.size(); ++i) {
        cout << vec[i] << " ";
    }
    cout << endl;
}

int main() {
    // 1. 创建空vector
    // 这是最基本的创建方式,创建一个不包含任何元素的vector
    vector<int> vec1;
    cout << "vec1 (空vector): ";
    printVector(vec1); // 输出为空

    // 2. 创建指定大小的vector,元素初始化为默认值
    // 这里创建包含5个整数的vector,每个元素默认初始化为0
    vector<int> vec2(5);
    cout << "vec2 (5个默认初始化的元素,默认为0): ";
    printVector(vec2); // 输出: 0 0 0 0 0

    // 3. 创建指定大小并指定初始值的vector
    // 创建包含3个元素的vector,每个元素的初始值都是100
    vector<int> vec3(3, 100);
    cout << "vec3 (3个元素,初始值均为100): ";
    printVector(vec3); // 输出: 100 100 100

    // 4. 使用初始化列表创建vector (C++11及以上)
    // 直接使用花括号初始化列表来创建并初始化vector
    vector<int> vec4 = {1, 2, 3, 4, 5};
    cout << "vec4 (初始化列表创建): ";
    printVector(vec4); // 输出: 1 2 3 4 5

    // 5. 使用数组初始化vector
    // 通过指定数组的起始地址和结束地址来初始化vector
    int arr[] = {10, 20, 30, 40, 50};
    vector<int> vec5(arr, arr + sizeof(arr) / sizeof(arr[0])); // 计算数组长度
    cout << "vec5 (通过数组创建): ";
    printVector(vec5); // 输出: 10 20 30 40 50

    // 6. 使用迭代器范围初始化vector
    // 使用另一个vector的迭代器范围来创建新的vector
    vector<int> vec6(vec4.begin(), vec4.begin() + 3); // 取vec4的前3个元素
    cout << "vec6 (迭代器范围创建): ";
    printVector(vec6); // 输出: 1 2 3

    // 7. 使用拷贝构造函数创建vector
    // 通过另一个vector创建一个完全相同的副本
    vector<int> vec7(vec4);
    cout << "vec7 (拷贝vec4创建): ";
    printVector(vec7); // 输出: 1 2 3 4 5

    // 8. 使用赋值运算符创建vector
    // 使用等号将另一个vector的内容赋值给新vector
    vector<int> vec8 = vec3;
    cout << "vec8 (赋值运算符创建): ";
    printVector(vec8); // 输出: 100 100 100

    return 0;
}
常见使用方法

C++ 中的 std::vector是一个功能强大的动态数组容器,它提供了丰富的方法来操作和管理数据。下面是一些最常用的方法,我会用表格先汇总核心方法,然后进行详细解释和代码示例。

方法类别 方法名 功能描述 时间复杂度
元素访问 operator[] 通过索引访问元素,不进行边界检查 O(1)
at(size_type pos) 通过索引访问元素,进行边界检查,越界抛出异常 O(1)
front() 返回第一个元素的引用 O(1)
back() 返回最后一个元素的引用 O(1)
data() 返回指向底层数组的指针 O(1)
容量查询 size() 返回当前元素个数 O(1)
capacity() 返回当前已分配的内存容量(可容纳元素个数) O(1)
empty() 判断容器是否为空 O(1)
修改操作 push_back(const T&) 在末尾添加一个元素 O(1)
emplace_back(Args...) 在末尾直接构造一个元素,避免拷贝 O(1)
pop_back() 删除末尾元素 O(1)
insert(iterator, ...) 在指定位置插入一个或多个元素 O(n)
erase(iterator) 删除指定位置的一个或一段元素 O(n)
clear() 清空所有元素 O(n)
容量管理 reserve(size_type n) 预分配至少容纳 n 个元素的内存空间 -
resize(size_type n) 调整容器的大小 -
shrink_to_fit() 请求移除未使用的容量,减少内存占用 -
迭代器 begin()/ end() 返回指向第一个元素/尾后位置的迭代器 O(1)
rbegin()/ rend() 返回反向迭代器 O(1)

🧠 详细说明与代码示例

1. 元素访问
  • operator[]at()都用于通过索引访问元素。at()会进行边界检查,如果索引越界会抛出 std::out_of_range异常,而 operator[]不进行边界检查,访问越界时行为未定义。

    c++ 复制代码
    std::vector<int> vec = {10, 20, 30};
    std::cout << vec[1];    // 输出 20
    std::cout << vec.at(1); // 输出 20
    // std::cout << vec.at(5); // 抛出 std::out_of_range 异常
  • front()back()分别返回首尾元素的引用。

    c++ 复制代码
    std::cout << vec.front(); // 输出 10
    std::cout << vec.back();  // 输出 30
  • data()返回指向底层数组的指针,便于与 C 风格函数交互。

    c++ 复制代码
    int* p = vec.data();
    std::cout << *(p + 1); // 输出 20
2. 容量查询
  • size()返回当前元素数量,capacity()返回当前已分配内存可容纳的元素数量,capacity()通常 >= size()

  • empty()检查容器是否为空。

    c++ 复制代码
    if (vec.empty()) {
        std::cout << "Vector is empty.";
    }
3. 修改操作
  • push_back()emplace_back()都在末尾添加元素。emplace_back()直接在容器内构造对象,对于非内置类型效率更高。

    c++ 复制代码
    vec.push_back(40); // 添加元素 40
    vec.emplace_back(50); // 直接构造元素 50
  • pop_back()删除末尾元素。

    c++ 复制代码
    vec.pop_back(); // 删除 50c++
  • insert()在指定迭代器位置前插入元素。它可以插入单个元素、多个相同元素、一个迭代器范围或初始化列表。

    c++ 复制代码
    // 在第二个元素(索引1)前插入 99
    auto it = vec.insert(vec.begin() + 1, 99); // vec: 10, 99, 20, 30, 40
    // 在位置 it 前插入 2 个 88
    vec.insert(it, 2, 88); // vec: 10, 88, 88, 99, 20, 30, 40
  • erase()删除指定位置或范围的元素。

    c++ 复制代码
    // 删除第三个元素(索引2)
    vec.erase(vec.begin() + 2);
    // 删除前两个元素 [vec.begin(), vec.begin()+2)
    vec.erase(vec.begin(), vec.begin() + 2);
  • clear()清空所有元素,但不释放容量

    c++ 复制代码
    vec.clear(); // size() 变为 0,capacity() 不变
4. 容量管理
  • reserve(n)预分配 至少可容纳 n个元素的内存空间,避免多次重新分配,提高性能。

    c++ 复制代码
    std::vector<int> vec;
    vec.reserve(1000); // 提前分配足够空间,避免添加1000个元素时多次扩容
    for (int i = 0; i < 1000; ++i) {
        vec.push_back(i);
    }
  • resize(n)调整 容器的大小。如果 n > size(),则在末尾添加默认值或指定值的元素;如果 n < size(),则截断多余元素。

    c++ 复制代码
    vec.resize(5);    // 如果size<5,则添加默认值0的元素;否则截断
    vec.resize(10, 1); // 如果size<10,则添加值为1的元素
  • shrink_to_fit()请求 释放未使用的内存容量,使 capacity()接近 size()。这是一个非强制性请求,实现可能会忽略。

    c++ 复制代码
    vec.shrink_to_fit(); // 希望减少容量至与size匹配
5. 迭代器

迭代器用于遍历容器。

  • begin()/ end():获取指向首元素和尾后元素的正向迭代器。
  • rbegin()/ rend():获取反向迭代器,用于逆序遍历。
  • C++11 起提供了 cbegin(), cend(), crbegin(), crend()返回常量迭代器。
c++ 复制代码
// 正向遍历
for (auto it = vec.begin(); it != vec.end(); ++it) {
    std::cout << *it << " ";
}
// 反向遍历
for (auto rit = vec.rbegin(); rit != vec.rend(); ++rit) {
    std::cout << *rit << " ";
}
// 基于范围的for循环 (C++11)
for (const auto& elem : vec) {
    std::cout << elem << " ";
}

重要注意事项

  1. 迭代器失效 :在进行修改操作(如 insert, erase, push_back(可能导致扩容))后,指向 vector 的迭代器、指针和引用可能会失效。切记不要在操作后使用旧的迭代器

  2. 算法兼容std::vector可与标准库算法(如 std::sort, std::find)完美配合。使用 #include <algorithm>

    c++ 复制代码
    std::sort(vec.begin(), vec.end()); // 排序
  3. 预分配空间 :如果能预估元素数量,使用 reserve()预先分配空间可以显著提高性能,避

  4. 免多次扩容和数据拷贝。