当前位置: 首页 > news >正文

C++ 第四阶段 STL 容器 - 第一讲:详解 std::vector

目录

📌 一、std::vector 核心特性

📌 二、常用函数详解(附代码示例)

1. 构造函数

2. 元素访问

3. 容量操作

4. 修改操作

📌 三、性能分析与优化建议

1. 动态扩容机制

2. 随机访问 vs. 插入/删除性能

3. 迭代器失效问题

📌 四、常见陷阱与解决方案

1. 未初始化元素访问

2. 不必要的拷贝

📌 五、典型应用场景

1. 动态数组

2. 算法实现

3. 二维数组模拟

🧠 总结

C++从入门到入土学习导航_c++学习进程-CSDN博客


📌 一、std::vector 核心特性

std::vector 是 C++ STL 中最常用的 序列式容器,其底层基于 动态数组 实现,具有以下核心特性:

特性说明
动态扩容内存自动增长(默认翻倍策略),无需手动管理内存。
连续存储元素存储在连续内存块中,支持指针偏移和随机访问。
随机访问时间复杂度为 O(1),可通过下标或 at() 访问任意元素。
尾部操作高效push_back()/pop_back() 时间复杂度为 O(1)(扩容时为 O(n))。
迭代器支持提供随机访问迭代器,兼容 STL 算法(如 sortfind)。

📌 二、常用函数详解(附代码示例)

1. 构造函数

#include <vector>
#include <iostream>int main() {// 1. 默认构造空 vectorstd::vector<int> v1;// 2. 构造指定大小和初始值std::vector<int> v2(5, 100);  // 5 个 100// 3. 迭代器构造std::vector<int> v3(v2.begin(), v2.end());  // 复制 v2 的内容// 4. 初始化列表(C++11)std::vector<int> v4 = {1, 2, 3, 4, 5};// 打印所有 vector 内容for (int i : v4) {std::cout << i << " ";}return 0;
}

2. 元素访问

std::vector<int> v = {10, 20, 30, 40, 50};// 1. 下标访问(不检查越界)
std::cout << "v[2] = " << v[2] << "\n";  // 输出 30// 2. at()(带边界检查)
std::cout << "v.at(3) = " << v.at(3) << "\n";  // 输出 40// 3. front() / back()
std::cout << "front: " << v.front() << "\n";  // 输出 10
std::cout << "back: " << v.back() << "\n";    // 输出 50// 4. data()(C++11,返回指针)
int* ptr = v.data();
std::cout << "*ptr = " << *ptr << "\n";  // 输出 10

3. 容量操作

std::vector<int> v = {1, 2, 3};// 1. size() / capacity()
std::cout << "size: " << v.size() << ", capacity: " << v.capacity() << "\n";// 2. reserve() 预分配内存
v.reserve(10);  // 扩容到至少 10 个元素的空间
std::cout << "capacity after reserve: " << v.capacity() << "\n";// 3. resize() 调整元素个数
v.resize(5, 0);  // 调整为 5 个元素,不足用 0 填充
for (int i : v) {std::cout << i << " ";  // 输出 1 2 3 0 0
}
std::cout << "\n";// 4. shrink_to_fit()(C++11)减少多余容量
v.shrink_to_fit();
std::cout << "capacity after shrink_to_fit: " << v.capacity() << "\n";

4. 修改操作

std::vector<int> v = {1, 2, 3, 4, 5};// 1. 尾部插入/删除
v.push_back(6);           // 插入到末尾
v.pop_back();             // 删除末尾元素// 2. emplace_back()(C++11,原地构造对象)
v.emplace_back(7);        // 更高效的插入(避免临时对象)// 3. 插入/删除任意位置
auto it = v.insert(v.begin() + 2, 99);  // 在第 3 个位置插入 99
std::cout << "Inserted value: " << *it << "\n";  // 输出 99v.erase(v.begin() + 1);   // 删除第 2 个元素// 4. 清空
v.clear();
std::cout << "size after clear: " << v.size() << "\n";  // 输出 0

📌 三、性能分析与优化建议

1. 动态扩容机制

  • 默认策略:扩容时分配 2 倍当前容量 的新内存,拷贝旧数据,释放旧内存。
  • 性能影响:频繁扩容会导致:
    • 内存碎片:频繁分配/释放内存。
    • 拷贝开销:大对象拷贝成本高。
  • 优化方案
    • 预分配内存:使用 reserve() 避免多次扩容。
    • 批量插入:优先使用 emplace_back() 而非 push_back()
// 示例:预分配内存减少扩容次数
std::vector<int> v;
v.reserve(1000);  // 一次性分配 1000 个元素的空间
for (int i = 0; i < 1000; ++i) {v.push_back(i);  // 无需扩容
}

2. 随机访问 vs. 插入/删除性能

操作类型时间复杂度说明
随机访问O(1)利用指针偏移直接访问元素。
尾部插入/删除O(1)(平均)默认扩容时为 O(n)。
中间插入/删除O(n)需移动元素,性能较差。
查找(find)O(n)无序数据需遍历;有序数据可用二分法。
// 示例:查找特定值
int target = 3;
auto it = std::find(v.begin(), v.end(), target);
if (it != v.end()) {std::cout << "Found at index: " << it - v.begin() << "\n";
} else {std::cout << "Not found\n";
}

3. 迭代器失效问题

  • 扩容导致迭代器失效push_back() 触发扩容后,所有迭代器失效。
  • 插入/删除导致迭代器失效insert()/erase() 后,迭代器需重新获取。
// 错误示例:扩容后迭代器失效
std::vector<int> v = {1, 2, 3};
auto it = v.begin();
v.push_back(4);  // 可能触发扩容,it 失效
std::cout << *it << "\n";  // 未定义行为!// 正确做法:重新获取迭代器
it = v.begin();
std::cout << *it << "\n";  // 安全

📌 四、常见陷阱与解决方案

1. 未初始化元素访问

  • 错误示例reserve() 不初始化元素,直接访问会导致未定义行为。
  • 正确做法:使用 resize() 初始化元素。
std::vector<int> v;
v.reserve(10);          // 仅分配内存,未初始化元素
std::cout << v[5];      // 未定义行为!v.resize(10);           // 初始化 10 个元素为 0
std::cout << v[5];      // 合法,输出 0

2. 不必要的拷贝

  • 推荐使用 emplace_back():直接构造对象,避免临时对象拷贝。
struct MyStruct {MyStruct(int a, int b) : x(a), y(b) {}int x, y;
};std::vector<MyStruct> v;// push_back 会创建临时对象再拷贝
v.push_back(MyStruct(1, 2));  // 两次构造(一次临时,一次拷贝)// emplace_back 原地构造,避免拷贝
v.emplace_back(1, 2);  // 一次构造

📌 五、典型应用场景

1. 动态数组

  • 替代静态数组,支持运行时调整大小。
  • 适用于数据量不确定的场景(如读取用户输入、动态数据收集)。
std::vector<int> nums;
int n;
while (std::cin >> n) {nums.push_back(n);  // 动态添加元素
}

2. 算法实现

  • 与 STL 算法无缝兼容(如 sortfindtransform)。
#include <algorithm>std::vector<int> v = {5, 2, 4, 1, 3};
std::sort(v.begin(), v.end());  // 排序
std::reverse(v.begin(), v.end());  // 反转

3. 二维数组模拟

  • 用于动态二维数组(如杨辉三角)。
std::vector<std::vector<int>> pascal(5);
for (int i = 0; i < 5; ++i) {pascal[i].resize(i + 1, 1);for (int j = 1; j < i; ++j) {pascal[i][j] = pascal[i-1][j] + pascal[i-1][j-1];}
}

🧠 总结

特性适用场景优势注意事项
动态扩容数据量不确定自动管理内存频繁扩容影响性能
连续存储需要随机访问缓存命中率高中间插入/删除慢
尾部操作高效栈/队列模拟插入/删除 O(1)扩容时迭代器失效
迭代器支持STL 算法兼容与 sortfind 等无缝对接插入/删除后需更新迭代器
http://www.lqws.cn/news/572635.html

相关文章:

  • 5 c++核心——文件操作
  • restful规范
  • Oauth2 自定义设置token过期时间
  • HarmonyOS 公共事件机制介绍以及多进程之间的通信实现(9000字详解)
  • 【网络】:DNS协议、ICMP协议、NAT技术
  • MongoDB06 - MongoDB 地理空间
  • vllm部署私有智谱大模型
  • 疏通经脉: Bridge 联通逻辑层和渲染层
  • 模拟多维物理过程与基于云的数值分析-AI云计算数值分析和代码验证
  • 生物实验室安全、化学品安全
  • 【notes2】并发,IO,内存
  • 30套精品论文答辩开题报告PPT模版
  • Gemini cli Quickstart
  • 数据结构复习4
  • 常用指令合集(DOS/Linux/git/Maven等)
  • debug的计算表达式
  • 《平行宇宙思维如何让前端错误处理无懈可击》
  • 2025年渗透测试面试题总结-2025年HW(护网面试) 20(题目+回答)
  • 各种常用的串口助手工具分享
  • 第10篇 图像语义分割和目标检测介绍
  • 循环神经网络的概念和案例
  • 带读YOLOv13,HyperACE | FullPAD到底是什么
  • 个人计算机系统安全、网络安全、数字加密与认证
  • 数据库中的 DDL(Data Definition Language,数据定义语言) 用于定义或修改数据库结构(如库、表、索引、约束等)。
  • 机器学习-02(深度学习的基本概念)
  • 智能新纪元:大语言模型如何重塑电商“人货场”经典范式
  • 【QT】信号和槽(1) 使用 || 定义
  • 深入学习 GORM:记录插入与数据检索
  • MySQL技巧
  • 【ad-hoc】# P12414 「YLLOI-R1-T3」一路向北|普及+