C++ 容器类 vector

C++ 容器类 中文讲解

关键要点

  • std::vector 是 C++ 标准模板库(STL)中的一种动态数组容器,研究表明它是处理可变大小数据集合的首选工具。
  • 它支持动态调整大小、随机访问和高效的尾部操作(如插入和删除),适合需要灵活存储的场景。
  • 提供丰富的成员函数,如 push_backpop_backsize 等,便于操作和遍历。
  • 相比 C 风格数组,std::vector 更安全且易于管理内存;相比 std::list,它提供更快的随机访问。

什么是 std::vector

std::vector 是一个动态数组容器,允许在运行时添加或删除元素。它就像一个可以自动扩展的数组,能够存储任意类型的对象,比如整数、字符串或自定义结构体。你可以把它想象成一个智能的数组,会自己管理内存,减少手动分配和释放的麻烦。

基本用法

要使用 std::vector,需要包含 <vector> 头文件。以下是一个简单示例,展示如何创建和操作向量:

#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3}; // 初始化一个包含 1, 2, 3 的向量
    vec.push_back(4); // 在末尾添加 4
    for (const auto& num : vec) {
        std::cout << num << " "; // 输出: 1 2 3 4
    }
    std::cout << std::endl;
    return 0;
}

主要功能

  • 动态扩展:可以随时添加或删除元素,自动调整内存。
  • 快速访问:通过下标(如 vec[0])快速访问元素。
  • 丰富接口:支持插入、删除、遍历等操作,兼容 STL 算法。

适用场景

std::vector 适合需要存储可变数量元素的场景,比如记录用户输入、存储动态数据或作为算法的输入数据。如果需要频繁在中间插入或删除元素,可能需要考虑其他容器如 std::list


详细报告

本文基于 2025 年 7 月 11 日的最新网络资源,全面解读 C++ 标准库 <vector> 的用法、特性和最佳实践,旨在为开发者提供清晰的指导。

背景与概述

在 C++ 编程中,管理动态数据集合是常见需求。传统 C 风格数组大小固定,无法动态调整,且容易发生越界访问问题。C++ 标准模板库(STL)中的 std::vector 是一个动态数组容器,自 C++98 起引入,提供了灵活的大小调整、随机访问和自动内存管理功能。研究表明,std::vector 是 C++ 中最常用的容器之一,广泛应用于需要动态存储的场景。

<vector> 的位置和作用

  • 文件位置<vector> 是 C++ 标准库的一部分,通常由编译器提供,无需额外安装。
  • 主要作用:定义 std::vector 容器,用于存储可变数量的元素,支持动态扩展和高效的随机访问。

std::vector 的定义与特性

std::vector 是一个模板类,定义如下:

template<class T, class Allocator = std::allocator<T>> class vector;
  • T:元素类型,可以是基本类型(如 intdouble)、字符串(std::string)或自定义结构体。
  • Allocator:内存分配器,默认使用 std::allocator

特性

  • 动态大小:可以根据需要自动增长或缩小。
  • 连续内存:元素存储在连续的内存块中,支持 O(1) 的随机访问。
  • 高效尾部操作:在末尾插入(push_back)或删除(pop_back)的复杂度为 O(1)。
  • 标准容器接口:支持迭代器、STL 算法(如 std::sortstd::find)。

std::vector 的基本用法

要使用 std::vector,需包含 <vector> 头文件,并使用 std 命名空间:

#include <vector>
using namespace std; // 或使用 std::vector
创建与初始化

std::vector 支持多种创建方式:

  • 空向量
  vector<int> vec; // 创建一个空的整数向量
  • 指定大小
  vector<double> vec(10); // 创建大小为 10 的向量,元素默认初始化为 0.0
  vector<double> vec(10, 1.0); // 创建大小为 10 的向量,元素初始化为 1.0
  • 初始化列表(C++11 起)
  vector<int> vec = {1, 2, 3, 4, 5}; // 初始化为 1, 2, 3, 4, 5
  • 从数组复制
  int arr[] = {1, 2, 3};
  vector<int> vec(arr, arr + 3); // 复制数组的前 3 个元素
  • 拷贝构造
  vector<int> vec1 = {1, 2, 3};
  vector<int> vec2(vec1); // vec2 是 vec1 的拷贝
成员函数

std::vector 提供了丰富的成员函数,涵盖元素访问、修改、容量管理和迭代器操作。以下是主要成员函数的分类和功能:

类别函数功能
构造vector()创建空向量。
vector(size_t n)创建大小为 n 的向量,元素默认初始化。
vector(size_t n, const T& val)创建大小为 n 的向量,元素初始化为 val
vector(const vector&)拷贝构造。
vector(iterator first, iterator last)从迭代器范围 [first, last) 构造。
访问at(size_t pos)返回位置 pos 的元素,带越界检查。
operator[](size_t pos)返回位置 pos 的元素,无越界检查。
front()返回第一个元素。
back()返回最后一个元素。
data()返回指向第一个元素的指针。
修改push_back(const T& x)在末尾添加元素 x
pop_back()删除最后一个元素。
insert(iterator pos, const T& x)pos 插入元素 x
erase(iterator pos)删除 pos 处的元素。
clear()清空所有元素。
resize(size_t n)调整大小为 n,新元素默认初始化。
resize(size_t n, const T& val)调整大小为 n,新元素初始化为 val
assign(size_t n, const T& val)将向量设置为 nval
assign(iterator first, iterator last)将向量设置为范围 [first, last)
emplace(iterator pos, Args&&... args)pos 构造元素(C++11)。
emplace_back(Args&&... args)在末尾构造元素(C++11)。
容量size()返回当前元素数量。
capacity()返回当前分配的容量。
max_size()返回最大可能元素数量。
reserve(size_t n)预留至少 n 个元素的容量。
shrink_to_fit()减少容量到当前大小(C++11)。
迭代器begin()返回指向第一个元素的迭代器。
end()返回指向最后一个元素后位置的迭代器。
rbegin()返回指向最后一个元素的反向迭代器。
rend()返回指向第一个元素前位置的反向迭代器。
示例代码

以下是一个综合示例,展示 std::vector 的常见操作:

#include <vector>
#include <iostream>
#include <algorithm>

int main() {
    // 初始化
    std::vector<int> vec = {1, 2, 3};

    // 添加元素
    vec.push_back(4); // 添加 4
    vec.emplace_back(5); // 构造并添加 5

    // 插入元素
    vec.insert(vec.begin() + 1, 10); // 在索引 1 插入 10

    // 删除元素
    vec.erase(vec.begin() + 2); // 删除索引 2 的元素
    vec.pop_back(); // 删除最后一个元素

    // 遍历
    std::cout << "向量内容: ";
    for (const auto& num : vec) {
        std::cout << num << " "; // 输出: 1 10 2 4
    }
    std::cout << std::endl;

    // 排序
    std::sort(vec.begin(), vec.end());
    std::cout << "排序后: ";
    for (const auto& num : vec) {
        std::cout << num << " "; // 输出: 1 2 4 10
    }
    std::cout << std::endl;

    // 容量管理
    std::cout << "大小: " << vec.size() << ", 容量: " << vec.capacity() << std::endl;
    vec.reserve(10); // 预留容量
    std::cout << "预留后容量: " << vec.capacity() << std::endl;

    return 0;
}

性能分析

  • 随机访问operator[]at() 的复杂度为 O(1)。
  • 尾部操作push_backpop_back 的平均复杂度为 O(1),但扩容可能导致 O(n)。
  • 中间插入/删除inserterase 的复杂度为 O(n),因为需要移动后续元素。
  • 扩容机制:当 size 超过 capacity 时,std::vector 会重新分配内存,通常容量翻倍(或 1.5 倍,视实现而定),导致元素拷贝。

与其他容器的比较

特性std::vectorstd::arraystd::list
大小调整动态调整固定大小动态调整
内存布局连续内存连续内存非连续(链表)
随机访问O(1)O(1)O(n)
插入/删除尾部 O(1),中间 O(n)不支持任意位置 O(1)
内存管理自动管理,需注意扩容无动态分配自动管理,无扩容问题

高级特性

  • C++11 特性
  • 初始化列表:支持 vector<int> vec = {1, 2, 3};
  • emplace 系列函数emplaceemplace_back 直接构造元素,减少拷贝开销。
  • shrink_to_fit:优化内存使用。
  • 迭代器失效:当向量重新分配内存(如 push_back 导致扩容)时,所有迭代器、指针和引用都会失效。
  • 自定义类型std::vector 可以存储自定义结构体,但需确保类型支持拷贝或移动操作。例如:
  struct Rect {
      int id, length, width;
      bool operator<(const Rect& other) const {
          if (id != other.id) return id < other.id;
          if (length != other.length) return length < other.length;
          return width < other.width;
      }
  };
  vector<Rect> vec;

最佳实践

  • 预留容量:使用 reserve(n) 预分配内存,减少扩容次数。
  • 使用 emplace_back:优先使用 emplace_back 而非 push_back,以减少构造和拷贝开销。
  • 避免频繁中间操作:如果需要频繁在中间插入或删除,考虑 std::liststd::deque
  • 检查越界:使用 at() 而非 [] 来访问元素,以避免未定义行为。
  • 注意迭代器失效:在修改向量后(如 push_backinsert),重新获取迭代器。

常见问题与解决方案

  • 越界访问:使用 at() 代替 [] 以启用越界检查。
  • 扩容性能问题:提前使用 reserve() 预留足够容量。
  • 迭代器失效:在可能导致扩容的操作后,重新获取迭代器。
  • 内存浪费:使用 shrink_to_fit() 减少多余容量。

总结与建议

std::vector 是 C++ 中处理动态数组的理想选择,结合了高效的随机访问和灵活的内存管理。通过本文的讲解,您应该能够掌握 std::vector 的基本用法、特性及最佳实践。建议在实际开发中根据需求选择合适的容器,并结合 STL 算法提升功能性。更多详细信息可参考以下资源:

开发者可根据具体需求进一步查阅官方文档或上述链接以获取更多细节。

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注