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

STL---vector详解(从使用到底层)

前言在我的C专栏里有一篇讲解string的文章里边的各种接口讲解的比较详细大家对使用有疑惑的可以去我的专栏里看重复的接口相似的使用我就不再过多介绍了本文主要讲vector的底层。vector简介vector就是一个会自动扩容的顺序表。在cplusplus里边它是一个模板类。template class T, class Alloc allocatorT class vector; // generic templatevector的使用构造函数关于第一个构造函数的解释explicit vector (const allocator_type alloc allocator_type());//声明也许会有人有疑惑明明已经有第二个模板参数了就算不想用它默认的那个空间配置器类型即allocatorT那模板显示实例化的时候显示传第二个模板参数就行了为什么构造函数里还要给参数答(首先要知道allocator_type()是typedef过的而且模板参数是类型函数参数是对象不是一个概念模板决定的是类型但决定不了模板里函数用这个类型的哪个对象。)模板第二个参数Alloc决定了「允许使用的分配器类型」构造函数参数只能传「该类型的对象」不能传其他类型不传构造函数参数时默认用「模板参数类型的默认对象」传构造函数参数时用的是「该类型下你指定的具体对象」但类型必须和模板参数一致vectorint v1;//第一个构造函数当它是无参构造就行vectorint v2(10, 1);vectorint v2(v2.begin(), v2.end());遍历for (size_t i 0; i 10; i) { cout v1[i] ; } cout endl; for (auto e : v1) { cout e ; } cout endl; vectorint::iterator it1 v1.begin(); while (it1 ! v1.end()) { cout (*it1) ; it1; } cout endl;Capacity模块这里唯一需要注意的一个点就是在vs底层是1.5倍扩容构造的空vector的容量就是0不像string有一个_Buf数组。Modifiers模块vector里并没有去实现头插头删原因就是由于效率太低但可以使用insert去实现头插的效果。void test_03() { vectorint v1(5, 2);// 2 2 2 2 2 //在某个位置前插入 v1.insert(v1.begin(), 5);// 5 2 2 2 2 2 v1.insert(v1.begin() 3, 6);// 5 2 2 6 2 2 2 //在某个值前插入---vector里边没有find函数因为算法库里的find就够使用了 //在v1的6这个值前边插入一个7 auto it1 find(v1.begin(), v1.end(), 6); v1.insert(it1, 7); for (auto e : v1) { cout e ; } cout endl; v1.erase(v1.begin()); v1.erase(v1.begin() 1, v1.begin() 3);//删除的区间是左闭右开的 for (auto e : v1) { cout e ; } cout endl; }vector的一些特殊使用string s;vectorchar v1;这两个的区别在哪?从底层的角度来说都是一个字符数组但是vectorchar仅仅只是说数组里边存着char没有\0的标识并且没有string里边针对字符串的各种处理接口。////void push_back (const value_type val);vectorstring v2;v2.push_back(xxc);上边的代码走的是隐式类型转换xxc作为const char*的字符串会先去构造一个string类型的临时对象而val引用这个临时对象将这个临时对象拷贝构造给vector内部此时的value_type就是string。关于范围for便利自定义类型的小注意点void test_01() { vectorstring v1; v1.push_back(xxc); v1.push_back(lqm); /* * 范围for的底层会转化成*iterator*iterator的结果是一个string类型的对象 * 不加引用就是调用的赋值运算符重载是深拷贝效率不高了 * 所以建议如果是自定义类型就加上引用去便利如果不需修改一同加上const * 对于内置类型由于引用底层也是转化成指针去开空间的所以加不加引用效果都不大 */ for (const auto e : v1) { cout e ; } }initializer_listinitializer_list是C11出的新语法它是一个模板类底层其实是一个数组并且有两个指针一个指向头一个指向尾通俗理解就是两个迭代器。vector支持initializer_list去构造。vector (initializer_listvalue_type il, const allocator_type alloc allocator_type());//initializer_List auto il1 { 1,2,3 };//相当于是initializer_listint il auto il2 { 1,2,3,4,5,6,7 }; /* * v1v2是直接调用vector的拷贝构造 * {12345}会直接被编译器识别为initializer_listint类型传参传给拷贝构造的第一个参数 * 构造函数底层用相当于用范围for将数据一个个push_back()到vector里边 * * v3v4相当于是隐式类型转换 * 先用initializer_list去构造一个vector临时对象再将这个临时对象拷贝构造给v3, v4 * 但是编译器底层会优化成直接构造 */ vectorint v1({1,2,3,4,5}); vectorint v2({1,2,3,4,5,6,7,8,9,10}); //vectorint v3{1,2,3,4,5};//另一种写法效果是跟下边的v3一样的 vectorint v3 { 1,2,3,4,5 }; vectorint v4 { 1,2,3,4,5,6,7,8,9,10 };vector底层实现vector.h#pragma once #includeassert.h #includeiostream #includeinitializer_list using namespace std; namespace xxc { templateclass T class vector { public: typedef T* iterator; typedef const T* const_iterator; iterator begin() { return _start; } iterator end() { return _finish; } const_iterator begin() const { return _start; } const_iterator end() const { return _finish; } //这个默认构造无法省略不写虽然它什么都没干但是由于写了拷贝构造编译器不会再自动生成构造函数 //这就导致我们要vectorint v;就无构造可用 //也可以vector() default;强制编译器生成默认构造不管有没有显示写构造 vector() {} //注意这里都是在声明的地方给缺省值的不给的话由于C标准没有规定给不给成员变量初始化 //不同平台对于初不初始化成员变量都是不确定的所以给缺省值比较安全 //不初始化如果成员变量里边是随机值那么reserve的时候就会有问题 vector(const vectorT v) { reserve(v.capacity()); for (auto e : v) { push_back(e); } } //initializer_list是支持迭代器的 vector(initializer_listT il) { reserve(il.size()); for (auto e : il) { push_back(e); } } //注下边两个函数在调用的时候会产生问题因为函数调用会去匹配最合适的 //xxc::vectorint v7(10, 1);此时会匹配到迭代器区间构造因为10和1都是int类型匹配到模板最合适 //n个val构造的参数一个是size_t一个是T参数不如迭代器区间构造匹配 //但迭代器区间构造里涉及到解引用就运行出错了 //解决方法就是再写一个最匹配的实例函数 //迭代器区间构造---为什么要用函数模板是因为可能要用到其他容器的迭代器区间来构造vector template class InputIterator vector(InputIterator first, InputIterator last) { while (first ! last) { push_back(*first); first; } } vector(int n, T val T()) { resize(n, val); } vector(size_t n, T val T()) { resize(n, val); } //如果vector为空再析构 ~vector() { if (_start) { delete[] _start; _start _finish _end_of_shorage nullptr; } } void swap(vectorT v) { std::swap(_start, v._start); std::swap(_finish, v._finish); std::swap(_end_of_shorage, v._end_of_shorage); } //现代写法---v1 v7v7先拷贝构造给vv跟v1再交换一下数据相当于就v7赋值给了v1最后函数结束v出作用域销毁 vectorT operator(vectorT v) { swap(v); return *this; } size_t size() const { return _finish - _start; } size_t capacity() const { return _end_of_shorage - _start; } void reserve(size_t n) { /* * 下边的_finish _start size();有问题 * 因为size()是去调用size()函数此时的_start已经不是原来的0了指向了新空间 * 但_finish还是原来的0两个相减计算出来的size()就不对了应该加上原来的size()才对 * 所以要先保存原来的size() */ //if (n capacity()) //{ // //异地扩容 // T* tmp new T[n]; // if (_start)//如果_start不为nullptr就拷贝旧数据过来 // { // memcpy(tmp, _start, size()); // } // _start tmp; // _finish _start size(); // _end_of_shorage _start n; //} if (n capacity()) { //异地扩容 T* tmp new T[n]; size_t old_size size(); if (_start)//如果_start不为nullptr就拷贝旧数据过来 { //memcpy不是深拷贝 //memcpy(tmp, _start, sizeof(T) * old_size); for (size_t i 0; i old_size; i) { tmp[i] _start[i];//内置类型浅拷贝自定义类型调用赋值运算符重载(深拷贝) } delete[] _start; } _start tmp; _finish _start old_size; _end_of_shorage _start n; } } T operator[](size_t i) { assert(i size()); return _start[i]; } const T operator[](size_t i) const { assert(i size()); return _start[i]; } iterator insert(iterator pos, const T x) { assert(pos _finish pos _start); //扩容 if (_finish _end_of_shorage) { //这里如果不更新pos迭代器扩容后会存在一个迭代器失效的问题 size_t len pos - _start; reserve(capacity() 0 ? 4 : 2 * capacity()); pos _start len; } //这里的挪动数据没有string的那么麻烦要去考虑头插的无限循环问题 //因为pos是迭代器往后再怎么减都不可能是0的循环的条件也不可能恒成立 iterator end _finish - 1; while (end pos) { *(end 1) *end; end--; } *pos x; _finish; return pos; } iterator erase(iterator pos) { assert(pos _start); assert(pos _finish); iterator it pos 1; while (it ! _finish) { *(it - 1) *it; it;//尽量用前置因为后置在底层是会有拷贝的Date类里边就有 } _finish--; return pos; } void push_back(const T x) { //扩容 if (_finish _end_of_shorage) { reserve(capacity() 0 ? 4 : 2 * capacity()); } *_finish x; _finish; } void pop_back() { //assert(_finish _start); assert(!empty()); _finish--; } bool empty() const { return _start _finish; } void clear() { _finish _start; } //T()是匿名对象也叫临时对象 void resize(size_t n, T val T()) { if (n size()) { reserve(n); while (_finish ! _start n) { *(_finish) val; _finish; } } else { _finish _start n; } } private: iterator _start nullptr; iterator _finish nullptr; iterator _end_of_shorage nullptr; }; }vector的一些小细节迭代器失效//迭代器失效 void test_03() { xxc::vectorint v1; v1.push_back(1); v1.push_back(2); v1.push_back(3); v1.push_back(4); //v1.push_back(5); v1.insert(v1.begin(), 0); v1.insert(v1.begin() 2, 0); Print(v1); int x; cin x; auto it find(v1.begin(), v1.end(), x); v1.insert(it, 100); *it 1000;//这里的it也是迭代器失效 /* * 扩容会让it失效 * 由于不知道什么时候扩容所以insert过后就认为其失效 * * 明明insert函数内部有对迭代器失效进行了处理为什么it还是失效了 * 因为insert函数的iterator是传值传值是拷贝改变不会影响it本身 * * 能不能改成传引用 * 不能因为像v1.insert(v1.begin(), 0);这种场景的v1.begin()是传值返回 * 返回的是临时对象具有常性那begin()传引用返回呢不行因为无法应对 * v1.insert(v1.begin() 2, 0);这种场景v1.begin() 2计算结果也是临时对象具有常性 * * 那insert函数参数的iterator如果改成const引用呢 * 不行加上constinsert函数内部的pos迭代器无法修改了 * * 综上失效的迭代器无法使用 * * 同理使用erase函数依旧认为是迭代器失效了vs下对迭代器会有严格的检查 * * 为了解决迭代器失效库里边的inserterase....会有一个返回值使用迭代器之后更新一下就可以使用了 */ //erase中迭代器失效的简单场景 // 要求删除所有偶数 auto it v1.begin(); while (it ! v1.end()) { if (*it % 2 0) { // erase也会迭代器失效失效的迭代器就不能再使用了 // 要重新赋值更新这个迭代器才能使用 it v1.erase(it); } else { it; } } }insert函数里的迭代器失效关于T()C里边对于内置类型也有构造函数目的就是为了在给像resize这种函数的缺省值的时候能用模板给缺省值。void test_04() { int a int();//0 int b(1);//1 int c int(10);//10 //其他类型也一样不给他初始化默认就初始化为00.0ASCII码为0的值即\0 }由浅拷贝引发的问题以下这是reserve没修改前的代码void reserve(size_t n) { if (n capacity()) { //异地扩容 T* tmp new T[n]; size_t old_size size(); if (_start)//如果_start不为nullptr就拷贝旧数据过来 { //memcpy不是深拷贝 memcpy(tmp, _start, sizeof(T) * old_size); delete[] _start; } _start tmp; _finish _start old_size; _end_of_shorage _start n; } }问题拷贝构造自定义类型譬如string的时候会有深拷贝的问题因为string内部也会指向空间由于拷贝构造是复用真正的问题出在reservememcpy没有深拷贝导致拷贝给tmp的数据不包括string内部指向的资源使得tmp和_start指向同一块资源在delete[] _start的时候不仅释放掉了vector内部的string并且string也会调用自己的析构函数去释放掉自己额外开辟的空间这样一来tmp指向的数据就变成随机值了紧接着_start tmp后续打印出来的就变成随机值了使用函数模板打印不同vector我们自己写的vector在xxc这个命名空间里边而库里边的vector在std这个命名空间里边如果我们在既想打印库里边的又想打印自己手动实现的vector同一个Print就无法实现功能此时要设计成模板。templateclass Container void Print(const Container v) { for (auto e : v) { cout e ; } cout endl; }

相关文章:

STL---vector详解(从使用到底层)

前言在我的C专栏里有一篇讲解string的文章,里边的各种接口讲解的比较详细,大家对使用有疑惑的可以去我的专栏里看,重复的接口相似的使用我就不再过多介绍了,本文主要讲vector的底层。vector简介vector就是一个会自动扩容的顺序表。…...

【优化升级版】2026在线工具箱源码系统|含字典/成语/查询工具+独立后台管理

温馨提示:文末有联系方式产品核心定位 【优化升级版】2026在线工具箱源码系统,是当前市面上功能最全、稳定性最强的PHP工具聚合平台之一。 本版本由专业团队深度修复并持续迭代,不仅兼容主流建站环境,更强化了SEO结构与蜘蛛抓取友…...

力扣Hot100系列21(Java)——[多维动态规划]总结(不同路径,最小路径和,最长回文子串,最长公共子序列, 编辑距离)

文章目录前言一、不同路径1.题目2.代码3.例子二、最小路径和1.题目2.代码3.例子三、最长回文子串1.题目2.代码3.例子四、最长公共子序列1.题目2.代码3.例子五、 编辑距离1.题目2.代码3.例子前言 本文记录力扣Hot100里面关于多维动态规划的五道题,包括常见解法和一些…...

AI应用架构师助力智能金融系统设计迈向新高度

AI应用架构师:如何重构智能金融系统的“技术基因”? 引言:传统金融系统的“智能焦虑”,你有吗? 凌晨3点,某银行风控部门的张经理还在盯着屏幕——今天又有3笔欺诈交易漏判了。传统的规则引擎已经堆了1000多…...

装修预算装修预算

软装 18000: 沙发 2000 边几 1000 窗帘 5000 餐桌 餐椅 2000 床2 8000 家电 34500: 冰箱 4000 电视机 3000 油烟机灶台热水器 7000 洗碗机 3000 洗衣机 烘干机 4500 扫地机器人 2500 空调3小1大 8000 灯 2500 其他消费3万&#x…...

Ubuntu 22.04外接NVIDIA显卡驱动安装

我的NUC缺一个强大的图形处理硬件, 于是把之前吃灰的显卡坞翻了出来, 发挥点余热, 但是在此之前, 因为开源驱动 nouveau 驱动只能提供基础显示功能,无法调用GPU的加速能力。所以我还需要 彻底禁用nouveau驱动 nouveau是Ubuntu默认的开源驱动,必须禁用&am…...

opencv4.2.0源码安装

git config --global url."https://github.com".insteadOf git://github.comsudo apt update sudo apt upgrade -y# 安装编译工具和依赖库 sudo apt install -y \build-essential \cmake \git \pkg-config \libgtk-3-dev \libavcodec-dev \libavformat-dev \libswsca…...

Ajax Fetch Axios三者的区别

Ajax Fetch Axios三者的区别 三者都用于网路请求,但是不同维度1,Ajax(Asynchronous Javascript and XML),一种技术统称2,Fetch,一个具体的API3,Axios,第三方库https://ax…...

【异常】OpenClaw线上服务器磁盘高位告警故障排查与解决指南 ⚠️ 线上业务节点 磁盘使用率88%(已连续11小时高位运行),建议尽快清理释放空间

一、报错内容 本次故障触发线上服务器监控系统告警,完整告警信息与应急初步处置结果如下: 核心告警条目 ⚠️ 线上业务节点 磁盘使用率88%(已连续11小时高位运行),建议尽快清理释放空间初步应急清理明细 通过临时冗余文件清理,完成首批空间释放,明细如下: 清理项目 预…...

假如后端一次性返回10w条数据,前端如何应对

假如后端一次性返回10w条数据首先设计不合理浏览器能否处理10w条数据1,JS没问题2,渲染到DOM会非常卡顿方案一:自定义中间层(1)自定义nodejs中间层,获取并拆分这10w条数据(2)前端对接…...

【异常】OpenClaw 项目 `fetch failed` 报错问题排查与解决方案Response interrupted: TypeError: fetch failed

OpenClaw 项目 fetch failed 报错问题排查与解决方案 一、报错内容 本次问题核心报错原文如下: Response interrupted: TypeError: fetch failedNode.js 运行环境下常见完整报错上下文(已脱敏): node:internal/deps/undici/undici:xxxxError.captureStackTrace(err, t…...

847-便捷视频剪切-视频片段删除合并工具V1.0

对单个视频文件进行剪辑操作,删除视频中的1个片段或多个片段,删除多个片段后自动合并未删除的视频片段,生成的视频为标准H264编码格式的mp4格式视频。 核心功能 视频播放与预览:拖入视频播放预览区间标记:可视化标记需…...

养龙虾-------【openclaw 对接小红书 】---自动化小红书

🚀 MiniMax Token Plan 惊喜上线!新增语音、音乐、视频和图片生成权益。邀请好友享双重好礼,助力开发体验! 好友立享 9折 专属优惠 Builder 权益,你赢返利 社区特权! 👉 立即参与:…...

告别SQL性能焦虑:教你如何解决

你是否遇到过这样的场景:一个看似复杂的SQL,在测试环境运行飞快,一到生产环境就“卡死”,一查执行计划,发现子查询生成了一个巨大的中间结果集,导致后续操作全部陷入性能泥潭?如果你正被此类场景…...

性能调优实战:数据库连接条件下推原理与案例拆解

文章目录引言一、问题背景1.1 客户场景中的典型痛点1.2 业界普遍面临的两大难点1.2.1 语义安全性(Equivalence)1.2.2 代价评估(Cost)二、传统方案的局限三、金仓数据库基于代价的连接条件下推设计3.1 能不能推:等价性判…...

Zed IDE新大招:Git 三合一 Picker,告别“找功能“焦虑症!

推荐阅读 Zed IDE 又整新活:确实比 VS Code 优雅丝滑! Zed IDE 又扔出了一个新玩具,确实比 VS Code 清新优雅! Zed 推出分栏 Diff :比 VSCode 更快、更智能的Git体验! Zed IDE 官宣ACP:一…...

LabVIEW后面板密码移除工具|支持全版本工程|一键清除保护密码

温馨提示:文末有联系方式工具核心功能:全版本LabVIEW后面板密码清除 本工具专为LabVIEW开发环境设计,可安全、稳定地清除LabVIEW VI文件的后面板保护密码。 全面兼容LabVIEW 2010至最新版2024,无论您使用的是32位或64位系统&#…...

告别复杂查询性能噩梦:一文读懂连接条件下推优化

摘要:金仓数据库(KingbaseES)的「基于代价的连接条件下推」技术解决了复杂SQL查询在生产环境中的性能瓶颈问题。该技术通过智能决策框架,先进行安全性检查确保语义等价,再基于代价模型评估下推收益,将连接条件智能下推到子查询中提…...

C语言写量子芯片驱动前必须做的7步接口压力测试:从单光子探测器误触发到多QPU并发访问崩溃的完整复现路径

第一章:C语言量子芯片接口测试的底层约束与物理边界C语言作为量子硬件接口层最广泛采用的系统编程语言,其与量子芯片(如超导量子处理器、硅基自旋量子点)的交互直接受限于物理层不可逾越的约束:纳秒级时序精度、亚毫伏…...

OJ前端页面开发

Markdown 编辑器 推荐的 Md 编辑器:https://github.com/bytedance/bytemd阅读官方文档,下载编辑器主体、以及 gfm(表格支持)插件、highlight 代码高亮插件 npm i bytemd/vue-next npm i bytemd/plugin-highlight bytemd/plugin-gf…...

梯形图转C后PLC宕机?别怪编译器!用这4个AST节点校验点+1张转换映射热力图,5分钟定位逻辑偏移根源

第一章:梯形图转C后PLC宕机?别怪编译器!用这4个AST节点校验点1张转换映射热力图,5分钟定位逻辑偏移根源当梯形图(LAD)经自动化工具转换为C代码部署至嵌入式PLC后突发宕机,多数工程师第一反应是质…...

大疆司空平台接入实战:OpenAPI无权限异常排查

前言 调用大疆司空 2 OpenAPI 接口时,很多开发者都会遇到 403 无权限异常 这个问题。本文基于实际项目排错经验,整理了完整的排查步骤,帮助你快速定位并解决问题。 403 错误是大疆 OpenAPI 开发中最常见的问题之一,据统计&#…...

100.【SV】SystemVerilog Interview Questions Set 1

📘 SystemVerilog 面试题集 1 —— 验证工程师的“知识快充” 在芯片验证面试中,面试官常常会考察你对 SystemVerilog 核心概念的理解。这些问题覆盖了从面向对象编程到并发控制,再到 DPI 等高级特性。今天,就来逐一解析这些常见面…...

现代智能汽车系统——三电2

2026年新能源汽车驱动电机技术呈现三大发展趋势:1.技术路线多元化,永磁同步电机(95%效率)仍为主流,励磁同步电机(无稀土依赖)成为欧洲新宠;2.核心技术创新,扁线绕组&…...

告别熬夜做 PPT:Paperzz AI PPT 生成器,15 分钟搞定毕业论文答辩全场景模板

Paperzz-AI官网免费论文查重复率AIGC检测/开题报告/文献综述/论文初稿/AI PPTpaperzz - AI PPT制作https://www.paperzz.cc/aiPpt 在毕业论文答辩的筹备流程中,PPT 往往是最容易被忽视却最关键的环节。很多毕业生花费数周打磨论文内容,却在制作答辩 PPT …...

结合Abaqus和Matlab建立理想的三维多晶模型:可自由选择模型尺寸和晶粒数量

结合abaqus和matlab建立理想的三维多晶模型,可自由选择模型尺寸和晶粒数量。在工程仿真领域,建立精确的模型是进行有效分析的关键。今天,我们来聊聊如何结合Abaqus和Matlab来构建一个理想的三维多晶模型。这种模型不仅可以根据需要自由选择尺…...

git 导出提交记录

git log develop --dateiso --prettyformat:"%h","%an","%ad","%s" --author作者 --since30 day ago > log.csvgit log 分支 --date时间格式 --pretty导出格式 --author作者 --since单引号内写导出时间 > 导出为 xx.csv\txt\log…...

程序员效率提升:IDEA 神级插件 + 配置,开发速度翻倍

从"重复搬砖"到"高效编码":IDEA 神级插件配置,开发速度直接翻倍 作为常年和IDEA打交道的后端开发者,我太懂那种被重复代码、格式问题、API调试拖慢节奏的痛苦。本文整理了我实测半年、能直接落地的7个神级插件6项核心配置…...

【仅限首批200家通过MCP 2026认证的企业可见】:OAuth 2026 FAPI-RW+eKYC联合认证沙箱实操——含FIDO2 attestation绑定与CIBA反向推送完整链路

第一章:【仅限首批200家通过MCP 2026认证的企业可见】:OAuth 2026 FAPI-RWeKYC联合认证沙箱实操——含FIDO2 attestation绑定与CIBA反向推送完整链路本章节面向已获准入资格的首批200家MCP 2026认证企业,提供OAuth 2026 FAPI-RW规范与eKYC身份…...

SpringBoot 3.3 整合 AI 接口:5 分钟快速实现智能应用

为什么要做 SpringBoot 3.3 AI 整合? SpringBoot 3.3 作为最新稳定版,不仅强化了原生虚拟线程、AOT 编译等性能特性,还对 HTTP 客户端、配置体系做了轻量化优化。而当前 AI 应用的核心痛点之一就是快速落地——大多数开发者不需要从零搭建 …...