
在 C 标准库中vector 是最常用的容器之一它本质上是一个动态顺序表兼具数组的随机访问特性和动态扩容的灵活性。本文将从基础使用、核心接口、迭代器失效、底层实现等维度全面拆解 vector 的核心知识点帮你彻底吃透这个容器。一、vector 基础使用1. 头文件与初始化使用 vector 首先需要包含头文件vector它的初始化方式灵活多样适配不同场景12345678910111213#include iostream#include vectorusingnamespacestd;voidtest_vector_init() {// 1. 空vectorvectorint v1;// 2. 初始化10个元素每个元素值为1vectorint v2(10, 1);// 3. 用v2的迭代器区间初始化v3拷贝v2所有元素vectorint v3(v2.begin(), v2.end());// 4. 列表初始化C11及以上vectorint v4{1, 2, 0};}2. 三种遍历方式vector 的遍历和 string 高度相似支持下标、迭代器、范围 for 三种方式1234567891011121314151617181920voidtest_vector_traverse() {vectorint v{1, 2, 3, 4, 5};// 方式1下标遍历随机访问特性for(size_ti 0; i v.size(); i) {cout v[i] ;}cout endl;// 方式2迭代器遍历vectorint::iterator it v.begin();while(it ! v.end()) {cout *it ;it;}cout endl;// 方式3范围forC11及以上for(auto e : v) {cout e ;}cout endl;}3. 插入与删除vector 的插入insert和删除erase是高频操作需注意迭代器的使用规则123456789101112131415161718192021voidtest_vector_insert_erase() {vectorint v(10, 1);// 尾插元素2v.push_back(2);// 头插元素3insert仅支持迭代器传参v.insert(v.begin(), 3);// 在第5个位置前插入4迭代器偏移v.insert(v.begin() 5, 4);for(auto e : v) {cout e ;// 输出3 1 1 1 1 4 1 1 1 1 1 1 2}cout endl;// 尾删元素v.pop_back();// 删除第5个位置的元素v.erase(v.begin() 5);for(auto e : v) {cout e ;// 输出3 1 1 1 1 1 1 1 1 1 1}cout endl;}4. 二维 vector二维数组vector 支持嵌套定义实现二维数组相比原生二维数组更灵活每行长度可不同1234567891011121314voidtest_vector_2d() {// 初始化10行5列的二维数组每个元素初始值为1vectorint v(5, 1);vectorvectorint vv(10, v);// 修改第3行第2列的元素下标从0开始vv[2][1] 2;// 遍历二维vectorfor(size_ti 0; i vv.size(); i) {for(size_tj 0; j vv[i].size(); j) {cout vv[i][j] ;}cout endl;}}5. 实用案例杨辉三角结合 vector 的初始化、resize、元素访问等特性实现 LeetCode 经典题 “杨辉三角”1234567891011121314151617181920classSolution {public:vectorvectorint generate(intnumRows) {// 初始化numRows行的二维vectorvectorvectorint vv(numRows);for(size_ti 0; i numRows; i) {// 第i行开辟i1个空间初始值为0vv[i].resize(i1, 0);// 每行首尾元素设为1vv[i].front() vv[i].back() 1;}// 填充中间元素for(inti 1; i vv.size(); i) {for(intj 1; j vv[i].size()-1; j) {vv[i][j] vv[i-1][j-1] vv[i-1][j];}}returnvv;}};二、vector 核心接口对比与细节1. reserve仅扩容不初始化vector 的reserve和 string 的reserve存在关键区别当n size()时vector 的 reserve无任何操作string 的 reserve 若缩容多数编译器忽略至少保证容量不低于 size ()。2. resize改变元素个数按需初始化resize会直接修改 vector 的元素个数行为分三种情况n size()删除末尾元素仅调整有效元素个数size() n capacity()在末尾插入元素用默认值初始化n capacity()先扩容再插入元素初始化。注意vector 默认按元素类型的默认值初始化如 int 为 0而 string 固定初始化为\0。12// resize接口原型可指定初始化值voidresize (size_type n, value_type val value_type());3. 不支持重载 和vector 没有默认的输入输出重载需手动实现输入输出逻辑12345678910// 单个元素输入vectorint v;intx;cin x;v.push_back(x);// 多个元素输入初始化5个元素vectorint v1(5, 0);for(size_ti 0; i 5; i) {cin v1[i];}三、迭代器失效迭代器失效是 vector 使用中最容易出错的地方本质是迭代器指向的空间失效或指向位置的语义改变主要分为两类场景1. 扩容导致的迭代器失效野指针当 vector 插入元素触发扩容时原空间会被释放之前的迭代器变为野指针12345678910// 错误示例扩容后迭代器失效voidtest_vector_invalid1() {vectorint v{1,2,3,4};// 插入位置迭代器auto pos v.begin() 1;// 插入元素触发扩容原pos指向的空间被释放v.insert(pos, 5);// 访问失效迭代器结果未定义cout *pos endl;}解决方法插入时记录迭代器相对位置扩容后重新修正迭代器123456789101112131415161718192021iterator insert(iterator pos,constT x) {assert(pos _start pos _finish);if(_finish _end_of_storage) {// 记录相对位置size_tlen pos - _start;// 扩容reserve(capacity() 0 ? 4 : 2 * capacity());// 修正迭代器指向新空间pos _start len;}// 元素后移 插入新元素iterator end _finish - 1;while(end pos) {*(end 1) *end;--end;}*pos x;_finish;// 返回新插入元素的迭代器returnpos;}2. 插入 / 删除导致的迭代器语义失效1insert 后迭代器语义失效insert 会挪动元素原迭代器指向的位置语义改变不再是原来的元素12345678910voidtest_vector_invalid2() {vectorint v{1,5,2,3,4};auto p find(v.begin(), v.end(), 2);if(p ! v.end()) {// 插入后p不再指向2而是指向新插入的40v.insert(p, 40);// 错误修改的是40而非原来的2(*p) * 10;}}解决方法接收 insert 返回的新迭代器重新定位1234567auto p find(v.begin(), v.end(), 2);if(p ! v.end()) {// 接收新迭代器指向插入的40p v.insert(p, 40);// 修改原来的2p1位置(*(p 1)) * 10;}2erase 后迭代器语义失效erase 删除元素后原迭代器指向的位置变为下一个元素若直接 会跳过元素或死循环1234567891011// 错误示例删除所有偶数迭代器失效导致漏删/死循环voidtest_vector_invalid3() {vectorint v{1,2,3,4};auto it v.begin();while(it ! v.end()) {if(*it % 2 0) {v.erase(it);// 删除后it失效}it;// 失效迭代器行为未定义}}解决方法接收 erase 返回的迭代器指向删除元素的下一个位置123456789101112131415// 正确版本删除所有偶数voidtest_vector_erase_fix() {vectorint v{1,2,3,4};auto it v.begin();while(it ! v.end()) {if(*it % 2 0) {// 接收返回值更新迭代器it v.erase(it);}else{it;// 仅非删除场景}}// 输出1 3for(auto e : v) cout e ;}四、vector 底层实现关键细节1. 迭代器区间构造模板复用vector 的迭代器区间构造函数设计为模板函数支持任意容器的迭代器初始化只要类型匹配1234567891011// 类模板的成员函数可嵌套函数模板templateclassInputIteratorvector(InputIterator first, InputIterator last) {while(first ! last) {push_back(*first);first;}}// 用法仅拷贝v1的前3个元素vectorint v1{1,2,3,4,5};vectorint v2(v1.begin(), v1.begin()3);2. 浅拷贝问题修复vector 扩容时若用 memcpy 拷贝元素会导致自定义类型如 string的浅拷贝问题需改用赋值运算符12345678910111213141516voidreserve(size_tn) {if(n capacity()) {size_told_size size();T* tmp newT[n];// 错误memcpy是浅拷贝自定义类型会析构重复释放// memcpy(tmp, _start, size() * sizeof(T));// 正确调用T的赋值运算符深拷贝自定义类型for(size_ti 0; i old_size; i) {tmp[i] _start[i];}delete[] _start;_start tmp;_finish _start old_size;_end_of_storage _start n;}}3. 内置类型的 “伪构造函数”vector 的 resize 接口默认参数为T()为了适配内置类型如 intC 编译器会为内置类型模拟构造函数12345678910111213voidresize(size_tn, T val T()) {if(n size()) {_finish _start n;}else{reserve(n);while(_finish _start n) {*_finish val;_finish;}}}// 调用示例int()等价于0double()等价于0.0v.resize(10);// 内置类型用默认值初始化4. const_iterator 的 typename 修饰在模板中使用vectorT::const_iterator时需加typename说明这是类型否则编译器视为静态成员123456// 错误编译器无法区分const_iterator是类型还是成员// vectorT::const_iterator it v.begin();// 正确1加typename说明是类型typenamevectorT::const_iterator it1 v.begin();// 正确2用auto自动推导推荐auto it2 v.begin();到此这篇关于C vector从使用到底层核心剖析的文章就介绍到这了,