news 2026/8/30 5:59:25

从零了解Vector详细解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零了解Vector详细解析

与string的衔接

顺序表 vector ,是一个标准的模板

vector是一个双参数模板:

  • 第一个参数:T→ 容器里面存的数据类型(int/Channel

  • 第二个参数:Alloc→ 内存分配器,默认值就是allocator<T>

2.allocator<T>是什么

std::allocator是 C++ STL 默认内存管理器。

vector 需要堆内存存放元素;allocator的工作就是:向操作系统申请内存、释放内存。

它封装了底层的:

  • new/delete

  • malloc/free

它干两件核心工作

  1. 分配原始内存:只开辟一块裸内存,不调用构造函数

  2. 释放内存:归还内存,不调用析构函数

区分两个概念

  • 分配内存:找一块空地

  • 构造对象:在这块空地上调用类的构造函数

emplace_back:allocator 先分配内存 → 然后原地构造对象,正好就是你之前代码_channels.emplace_back(wfd,subid)的完整流程。

而 string 是一个具体类型,在库中string本质为 basic_string <char> 的别名

vector是类模板,类模板必须显示实例化

std::vector<T>—— 这是模板(图纸),不是一个可用的类型。

显式实例化:给模板参数传值,造出一个真实的类(成品)

std::vector<int> v1; // 显式实例化,得到一个实实在在的类 std::vector<int> v2(10,1); std::vector<int> v3(v2.begin(),v2.end());

下面这个写法根本不能用来定义变量

为没有显式实例化,就是只写了模板名字,没有给尖括号传类型参数

std::vector; // 报错!!没有填模板参数T

遍历vector的方法:

用迭代器:

vector<int>::iterator it = v3.begin();

范围for:

for(auto e;v3) { //... }

vector不会缩容

而string会缩容:重新分配更小的内存 + 拷贝数据 + 释放旧的大内存

(操作系统的堆内存管理规则是:分配的内存块必须整体释放,无法拆分归还)

vector::resize

把vector的数据个数改为 n(size也会变)

<size:删除数据

>size:插入数据,空间不够就扩容

vector::operator[]

operator[]就是下标运算符重载

v3[i]编译器等价翻译成:v3.operator[](i)

1.两个重载版本
① 非 const 版本 —— 可读、可修改元素
int &a = v3[0]; a = 666; //可以修改容器里面的对象

返回值:引用reference→ int&

② const 版本(容器被 const 修饰时调用),只读
const std::vector<int>& b = v3; const int& c = b[0]; // c = 999; //报错,不能修改
2.operator [] 最核心特点:不做越界检查
std::vector<int> v{10,20,30}; v[100] = 999; //下标100远远超出范围

编译不会报错,运行时不会抛出异常,直接访问非法内存 → 未定义行为 (UB),程序可能随机崩溃、乱改内存。

这是和.at()的最大区别

方法越界检查越界行为
vec[i]operator[]无检查未定义行为,危险
vec.at(i)边界检查抛出 std::out_of_range 异常,安全

示例对比

v.at(100); //越界 →抛异常,可以try‑catch捕获 v[100]; //越界 →直接野内存访问,无法捕获
3.返回引用
std::vector<int> v = {1,2,3};int x = v[0]; // 值拷贝,x是副本,改x不会影响vint &y = v[0]; // 拿到容器内部元素的引用,改y就改v内部元素 y = 99;// v[0] 变成99
4.迭代器 / 引用失效大坑(网络编程必踩坑)

当你调用push_back / emplace_back触发 vector 扩容

之前用operator[]获取到的引用Channel& ch = _channels[0];直接失效!变成野引用,再使用就是 UB 崩溃。

例子:

std::vector<int> v{1,2,3};int &ref = v[0]; v.reserve(100); //扩容!内存搬家! ref = 99; //野引用!未定义行为!
5.底层原理(vector 内存连续)

vector 的元素在内存一块连续数组。operator[]底层实现逻辑简化:

//伪代码 T& operator[](size_t pos) { return *( _start + pos ); }

_start指向数组首地址,下标就是指针偏移。

所以vec[2]*(vec.begin()+2)完全等价。

6.什么时候用 [],什么时候用 at ()

  1. 你已经手动保证下标一定合法 → 用[],速度更快,无额外检查开销(循环遍历 i 从 0 到 size ()-1)

for(int i=0;i<_channels.size();i++) { auto &ch = _channels[i]; //安全,i一定合法 }
  1. 下标来自外部输入、不确定是否合法 → 用.at(),开启越界保护

vector::insert

insert:在指定迭代器位置,插入一个 / 一批元素。

注意:vector 内存连续,插入中间位置,后面所有元素向后移位,效率低 O (n)

重载 1:插入 n 个相同的值
iterator insert(iterator pos, size_type count, const T& value);
std::vector<int> v = {1,2,3}; v.insert(v.begin(), 3, 88);// 在最前面插入3个88// v: 88,88,88,1,2,3
重载 2:插入一段区间 [first, last)
template<class InputIt> iterator insert(iterator pos, InputIt first, InputIt last);
std::vector<int> a = {1,2,3}; std::vector<int> b = {100,200};// 在a的尾部,插入b的全部元素 a.insert(a.end(), b.begin(), b.end());// a: 1,2,3,100,200
重载 3:初始化列表 C++11
v.insert(v.begin(), {5,6,7});
返回值极其重要(大坑!迭代器失效)

vector 一旦在中间insert,有可能触发扩容,所有旧迭代器全部失效!

std::vector<int> v = {1,2,3};auto it = v.begin()+1; v.insert(it, 99); //插入后,it失效!!不能再用it!// 正确做法:用insert返回的新迭代器 it = v.insert(v.begin()+1, 99);

不要保存旧迭代器;insert返回新迭代器,赋值回去。

vector::erase

erase的作用:删除 vector 中一个或者一段区间的元素

注意: vector 内存连续,删除中间元素后,后面所有元素必须向前移动补齐空位,时间复杂度 O (n);并且会造成迭代器失效,这是最高频的坑

重载 1:删除单个元素(传迭代器)
iterator erase(iterator pos);
重载 2:删除一段区间,左闭右开

[first, last)

iterator erase(iterator first, iterator last);

返回值:返回被删除元素的下一个位置的新迭代器

最经典坑:一边遍历一边删除

错误代码(失效崩溃)

std::vector<int> v = {1, 2, 2, 3}; for(auto it = v.begin(); it != v.end(); it++) { if(*it == 2) { v.erase(it); // erase后it立刻失效!循环下一次it++直接崩溃 } }

正确写法(利用 erase 返回值)

for(auto it = v.begin(); it != v.end(); ) { if(*it == 2) { it = v.erase(it); //接收返回的有效迭代器,不做it++ } else { ++it; } }

原理:erase返回删除点后面合法的迭代器,不需要再it++

vector::push_back

在 vector 的尾部追加一个元素

// 范围for for (auto e : v2) { cout << e << endl; } //其中 e 得到的是v2中所存的类型的深拷贝,而v2类型为string,拷贝效率低 ——> 用引用 for (const auto &e : v2)
for循环
// 二维数组,如4*5的 vector<int> v(5, 1); vector<vector<int>> v2(4, v); for (auto e : v2) { for (auto e1 : e) { cout << e1 << " "; } cout << endl; }

简易版vector的底层模型(STL 源码思路,简化掉分配器 allocator)

template <class T> class vector { private: T *_a; size_t _size; size_t _capacity; };
  1. _a

_a 是一个指针,类型:T*

_a 保存堆上那块连续数组内存的起始地址。

  • vector 的所有元素,并不存在栈上;而是在堆 (heap) 上面开辟一块连续的内存存放。

  • _a 就是指向这块堆数组第一个元素的指针。

  1. 三个成员变量一一对应含义
成员含义
T* _a内存起始指针,堆数组首地址
size_t _size当前元素个数。你能访问的有效元素数量。v.size() 返回的值
size_t _capacity已经分配的内存总容量。这块堆内存一共可以放下多少个 T 对象。v.capacity()

内存布局示意图:

堆内存: [ T0 ][ T1 ][ T2 ][ T3 ][ 空闲 ][ 空闲 ] ↑ _a _size = 3 //有效元素0,1,2 _capacity = 6 //整块内存一共能存6个元素

vector下标运算符重载

//可读可写,非const对象调用 T& operator[](int i) { return _a[i]; } //只读版本,const对象调用,不能修改元素 const T& operator[](int i) const { return _a[i]; }
下标运算符operator[]底层怎么实现

依靠_a指针偏移

T& operator[](size_t pos) { return _a[pos]; //等价于 *( _a + pos ) }

_a+pos:指针向后偏移 pos 个 T 类型的距离,找到对应元素。 这也就是为什么vector下标访问速度极快。

push_back 扩容时_a发生了什么
  1. 判断:_size == _capacity→ 内存满了,必须扩容

  2. 开辟一块更大的新堆内存,得到新指针T* new_a

  3. _a指向的旧内存所有元素拷贝到new_a

  4. 释放旧的 _a 指向的堆内存

  5. _a = new_a;,让指针指向新内存

  6. _capacity更新为新容量

  7. 在尾部放入新元素,_size++

扩容后旧的_a内存被释放! 所以之前保存的引用、迭代器(本质就是基于旧_a的指针)全部失效。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/30 5:59:16

网易深度学习算法岗笔试题复盘:从逻辑回归到KMP的备考路线

2018年网易校招深度学习算法工程师的笔试卷&#xff0c;到现在我偶尔还会拿出来让准备校招的同学做一遍。不是因为题目多新&#xff0c;恰恰相反&#xff0c;整套卷子里几乎没有追热点式的偏题怪题&#xff0c;翻来覆去考的就是那些越基础越容易忽略的东西——逻辑回归的梯度推…

作者头像 李华
网站建设 2026/8/30 5:58:51

警惕!只会敲命令的Linux运维将被淘汰,不懂安全的你没有未来了

一、 那个让我们引以为傲的“铁饭碗”&#xff0c;裂了一条缝数日前, 有一位身为做了5年运维之老友, 去面试大厂却遭遇失败。之后便回来找我一起喝酒。他话语中讲, 说: “当时面试官询问我, ‘你们公司的规则究竟是谁撰写的? 要是有人企图绕过WAF注入, 你会怎样从系统层面去溯…

作者头像 李华
网站建设 2026/8/30 5:57:12

DeepSeek+Codex CLI:一句话生成LaTeX Beamer PPT的实战指南

这次我们看一个非常实用的组合&#xff1a;DeepSeek 提供大模型推理能力&#xff0c;Codex CLI 负责命令行智能体执行&#xff0c;最后用一句自然语言指令把 LaTeX Beamer 幻灯片直接生成出来。以前做 PPT&#xff0c;要么手动排版&#xff0c;要么套模板找版式&#xff0c;要么…

作者头像 李华
网站建设 2026/8/30 5:55:51

具身智能入门路线与数据清洗:从树莓派小车到商业化落地

最近一段时间&#xff0c;“具身智能”几乎成了科技投资圈出现频率最高的词。从融资节奏看&#xff0c;这个赛道今年确实热得很快——985高校教授带团队创业&#xff0c;一批又一批地出现&#xff1b;从公开披露的融资事件统计来看&#xff0c;全年已有超过百亿元人民币涌入具身…

作者头像 李华
网站建设 2026/8/30 5:54:24

Transformer雷达回波外推实战:短临降水预测从数据到模型全解析

简介&#xff1a;本资源是一项面向计算机、人工智能及气象信息相关专业学生的高分毕业设计成果&#xff0c;聚焦雷达回波序列的短期降水预测任务&#xff0c;采用Transformer架构实现端到端的时空特征建模与外推。资源适用于课程设计、毕设参考及AI气象交叉方向入门与进阶学习&…

作者头像 李华
网站建设 2026/8/30 5:53:51

AI异构计算工程师必知:从体系结构到CUDA性能优化

2018年百度校招AI异构计算工程师的笔试题&#xff0c;第二批&#xff0c;这个话题放在今天看依然很有嚼头。当时那场笔试结束后&#xff0c;不少应届生在网上讨论题目难度&#xff0c;吐槽“CPU、GPU、缓存、CUDA全覆盖”&#xff0c;也有人感慨“面试造火箭&#xff0c;工作拧…

作者头像 李华