【C++】vector类常用接口及模拟实现

【C++】vector类常用接口及模拟实现

一、vector简介

vector在STL中表示顺序表,使用vector要包含vector头文件。
vector是一个模板,使用时要指定其中存储的数据类型(类模板只能显式实例化)。
vector不支持流插入和流提取,所以不能直接用cin、cout输入输出
vector文档介绍

二、vector的使用(常用接口)

1.vector的定义(构造方法)

(constructor)构造函数声明接口说明
vector()无参构造
vector(size_type n, const value_type& val = value_type())构造并初始化n个val
vector(const vector& x)拷贝构造
vector(InputIterator first, InputIterator last);使用迭代器进行初始化
#include<iostream>#include<vector>usingnamespace std;intmain(){// 默认构造 vector<int> v1;// 使用10个1初始化 vector<int>v2(10,1);// 使用v2的迭代器区间初始化 vector<int>v3(v2.begin(), v2.end());// 拷贝构造 vector<int>v4(v3);return0;}
调试结果

2.vector的遍历

vector的遍历有三种方法:下标+[]、迭代器、范围for

(1)下标+[]

#include<iostream>#include<vector>usingnamespace std;intmain(){ vector<int>v(10,1);for(size_t i =0; i < v.size(); i++){ cout << v[i]<<" ";} cout << endl;return0;}
运行结果

(2)迭代器

#include<iostream>#include<vector>usingnamespace std;intmain(){ vector<int>v(10,1); vector<int>::iterator it = v.begin();while(it != v.end()){ cout <<*it <<" ";++it;} cout << endl;return0;}
运行结果

(3)范围for

范围for的底层是迭代器

#include<iostream>#include<vector>usingnamespace std;intmain(){ vector<int>v(10,1);for(auto e:v){ cout << e <<" ";} cout << endl;return0;}
运行结果

3.vector类对象的容量操作

容量空间接口说明
size获取数据个数
capacity获取容量大小
empty判断是否为空
resize改变vector的size,同时对新增数据初始化(未指定就调用默认构造)。resize一般不会缩容,但会改变size:小于size时删除数据,大于size时插入数据,空间不够时会扩容
reserve改变vector的capacity(不缩容)
clear清除vector的元素

(1)resize

改变vector的size,同时对新增数据初始化(未指定就调用默认构造)。resize一般不会缩容,但会改变size:小于size时删除数据,大于size时插入数据,空间不够时会扩容

#include<iostream>#include<vector>usingnamespace std;intmain(){ vector<int>v(10,1);for(auto i : v){ cout << i <<" ";} cout << endl; cout << v.size()<< endl; cout << v.capacity()<< endl; v.reserve(20);for(auto i : v){ cout << i <<" ";} cout << endl; cout << v.size()<< endl; cout << v.capacity()<< endl; v.resize(15,2);for(auto i : v){ cout << i <<" ";} cout << endl; cout << v.size()<< endl; cout << v.capacity()<< endl; v.resize(5);for(auto i : v){ cout << i <<" ";} cout << endl; cout << v.size()<< endl; cout << v.capacity()<< endl;return0;}
运行结果

(2)capacity

#include<iostream>#include<vector>usingnamespace std;intmain(){ vector<int>v(10,1); cout << v.size()<< endl; cout << v.capacity()<< endl; v.reserve(20); cout << v.size()<< endl; cout << v.capacity()<< endl; v.reserve(15); cout << v.size()<< endl; cout << v.capacity()<< endl; v.reserve(5); cout << v.size()<< endl; cout << v.capacity()<< endl;return0;}
运行结果


从运行结果可以看出,把capacity改成20后,再使用reserve不会减小vector的capacity

4.vector增删查改

vector增删查改接口说明
push_back尾插
pop_back尾删
find查找(这个是算法模块实现,不是vector的成员接口)
insert在position之前插入val(只支持迭代器)
erase删除position位置的数据或删除一段迭代器区间(只支持迭代器)
swap交换两个vector的数据空间
operator[]像访问数组一样访问vector

(1)push_back和insert

#include<iostream>#include<vector>usingnamespace std;intmain(){ vector<int>v(10,1);// 使用push_back尾插元素 v.push_back(2);for(auto i : v){ cout << i <<" ";} cout << endl;// 在起始位置插入元素 v.insert(v.begin(),3);for(auto i : v){ cout << i <<" ";} cout << endl;// 在下标为3的位置插入元素 v.insert(v.begin()+3,4);for(auto i : v){ cout << i <<" ";} cout << endl;return0;}
运行结果

(2)erase

erase可以删除一个位置,也可以删除一段迭代器区间

#include<iostream>#include<vector>usingnamespace std;intmain(){ vector<int>v(10,1);// 删除起始位置 v.erase(v.begin());for(auto i : v){ cout << i <<" ";} cout << endl;// 删除下标为3的位置 v.erase(v.begin()+3);for(auto i : v){ cout << i <<" ";} cout << endl;return0;}
运行结果

5.vector的使用

vector不仅可以存储内置类型,还可以存储自定义类型甚至存储vector

使用vector存储vector

使用vector存储vector可以看作二维数组,也可以像访问二维数组一样使用[][]访问(实际上是两次函数调用)。

#include<iostream>#include<vector>usingnamespace std;intmain(){ vector<int>v(10,0);// 使用vector存储vector,相当于5行10列的二维数组 vector<vector<int>>vv(5, v);// 使用下标+[]访问vectorfor(int i =0; i < vv.size(); i++){for(int j =0; j < vv[i].size(); j++){ cout << vv[i][j]<<" ";} cout << endl;} cout << endl;return0;}
运行结果

三、vector迭代器失效问题

迭代器的主要作用是让算法能够不用关心底层数据结构,其底层就是一个指针,或者对指针进行了封装,比如vector的迭代器其实就是原生指针T*。因此迭代器失效实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间(类似野指针),造成的后果就是程序崩溃(即如果继续使用已经失效的迭代器,程序可能会崩溃
对于vector可能会导致其迭代器失效的操作有:

  1. 会引起其底层空间改变的操作,都有可能使迭代器失效,比如:resize、reserve、insert、assign、push_back等
#include<iostream>usingnamespace std;#include<vector>intmain(){ vector<int> v{1,2,3,4,5};auto it = v.begin();// 将有效元素个数加到100个,多出的位置用8填充,操作期间底层会扩容//v.resize(100,8)// reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容量改变//v.reservr(100)// 插入元素期间,可能会引起扩容,导致原空间被释放//v.insert(v.begin(), 0);//v.push_back(8)// 给vector重新赋值,可能会引起底层容量改变//v.assign(100, 8);/* 出错原因:以上操作都有可能导致vector扩容,也就是说vector底层原来的旧空间被释放掉,而在打印时,it还使用的释放之前的旧空间,在对it迭代器操作时,实际操作的是一块被释放掉的空间,而引起代码运行时崩溃 解决方式:在以上操作完成后,如果想要继续通过迭代器操作vector中的元素,只需给it重新赋值即可 */while(it != v.end()){ cout <<*it <<" ";++it;} cout << endl;return0;}
  1. 指定位置元素的删除操作
#include<iostream>usingnamespace std;#include<vector>intmain(){int a[]={1,2,3,4}; vector<int>v(a, a +sizeof(a)/sizeof(a[0]));// 使用find查找3所在位置的iterator vector<int>::iterator pos =find(v.begin(), v.end(),3);// 删除pos位置的数据,导致pos迭代器失效 v.erase(pos); cout <<*pos << endl;// 此处会导致非法访问return0;}

erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上迭代器不会失效,但是如果pos刚好是最后一个元素,删除之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。

  1. 注意:在Linux下,g++编译器对迭代器失效的检测并不是非常严格,处理也没有vs极端。在Linux下迭代器失效后,代码不一定会崩溃,但运行结果肯定不对,如果it不再begin到end范围内,程序一定会崩溃
  2. 与vector类似,string在插入+扩容操作+erase之后,迭代器也会失效
#include<iostream>usingnamespace std;#include<string>intmain(){ string s("hello");auto it = s.begin();// 放开之后程序会崩溃,因为resize到20string会进行扩容// 扩容之后,it指向之前的旧空间已经被释放了,该迭代器就会失效// 后序打印时,再访问it指向的空间,程序就会崩溃//s.resize(20, '!');while(it != s.end()){ cout <<*it;++it;} cout << endl; it = s.begin();while(it != s.end()){ it = s.erase(it);// 按照下面方式写,运行时程序会崩溃,因为在erase(it)之后,it位置的迭代器就失效了//s.erase(it);++it;}return0;}

迭代器失效的解决办法:在使用前对迭代器重新赋值即可。

四、Victor模拟实现

#pragmaonce#include<iostream>#include<assert.h>usingnamespace std;namespace zsy {template<classT>classvector{public:typedef T* iterator;typedefconst T * const_iterator;// 默认构造//vector() {}// c++11:强制生成默认构造vector()=default;// 拷贝构造vector(const vector<T>& v){reserve(v.size());for(auto& e : v){push_back(e);}}// 类模板的成员函数可以是函数模板// 迭代器区间构造(可以使用任意容器的迭代器初始化vector,但要求类型是匹配的)template<classInputIterator>vector(InputIterator first, InputIterator last){while(first != last){push_back(*first);++first;}}vector(size_t n,const T& val =T()){reserve(n);for(size_t i =0; i < n; i++){push_back(val);}}voidclear(){ _finish = _start;}// v1 = v3// 赋值重载// 传统写法//const vector<T>& operator=(const vector<T>& v) {// if (this != &v) {// clear();// reserve(v.size());// for (auto e : v) {// push_back(e);// }// }// return *this;//}voidswap(vector<T>& v){ std::swap(_start, v._start); std::swap(_finish, v._finish); std::swap(_end_of_storage, v._end_of_storage);}// 现代写法const vector<T>&operator=(vector<T> v){swap(v);return*this;}~vector(){if(_start){delete[] _start; _start = _finish = _end_of_storage =nullptr;}}// 迭代器 iterator begin(){return _start;} iterator end(){return _finish;} const_iterator begin()const{return _start;} const_iterator end()const{return _finish;}// 扩容voidreserve(size_t n){if(n >capacity()){ size_t old_size =size(); T* tmp =new T[n];// 使用memcpy会产生深拷贝问题//memcpy(tmp, _start, old_size * sizeof(T));// 遍历并一个个赋值可以解决深拷贝问题for(size_t i =0; i <old_size(); i++){ tmp[i]= _start[i];}delete[] _start; _start = tmp; _finish = tmp + old_size; _end_of_storage = _start + n;}}voidresize(size_t n,const T& val =T()){if(n <size()){ _finish = _start + n;}else{reserve(n);while(_finish <= _start + n){ _finish = val;++_finish;}}} size_t size()const{return _finish - _start;} size_t capacity()const{return _end_of_storage - _start;}boolempty(){return _start == _finish;}// 尾插voidpush_back(const T& x){// 如果满了就先扩容if(_finish == _end_of_storage){reserve(capacity()==0?4:capacity()*2);}*_finish = x;++_finish;}// 尾删voidpop_back(){assert(!empty());--_finish;}// 这种比较长的函数可以在类里面声明、类外面定义,但不能分成两个文件,否则会链接错误 iterator insert(iterator pos,const T& x){assert(pos >= _start);assert(pos <= _finish);// 如果满了就先扩容if(_finish == _end_of_storage){// 计算相对位置,扩容后对迭代器重新赋值可以解决迭代器失效问题 size_t len = pos - _start;reserve(capacity()==0?4:capacity()*2); pos = _start + len;} iterator end = _finish -1;while(end >= pos){*(end +1)=*end;--end;}*pos = x; _finish++;// insert之后迭代器会失效,要访问就要更新这个失效的迭代器// 返回新插入元素,可以解决迭代器失效问题return pos;} iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish); iterator it = pos +1;while(it !=end()){*(it -1)=*it;++it;}--_finish;return pos;} T&operator[](size_t i){assert(i <size());return _start[i];}const T&operator[](size_t i)const{assert(i <size());return _start[i];}private: iterator _start =nullptr; iterator _finish =nullptr; iterator _end_of_storage =nullptr;};template<classT>voidprint_vector(const vector<T>& v){// 规定:从未实例化的类模板中取东西,编译器不能区分这里的const_iterator是类型还是静态成员变量,需要加上typename//typename vector<T>::const_iterator it = v.begin();// 也可以直接将类型名写成autoauto it = v.begin();while(it != v.end()){ cout <<*it <<" ";++it;}}}

Read more

用 OpenClaw + 飞书,快速搭建 5 个可协作的 AI 助理团队

多个飞书机器人 + 独立工作空间 + 互相协作 = 专业化分工的 AI 助理团队 写在前面 如何用 OpenClaw 搭建一套多 Agent 系统,让 AI 助理各司其职、协同工作?通过 OpenClaw 多 Agent 架构,你可以实现: * 多个独立的飞书机器人,每个人设不同 * 各自独立的工作空间,数据完全隔离 * 可以互相协作,通过 agentToAgent 通信 * 共享长期记忆,跨渠道信息同步 本文将详细介绍如何在腾讯云服务器上,用 OpenClaw 搭建一套飞书多 Agent 系统,包括完整配置流程、常见问题解决方案和实战协作案例。 目录 1. 为什么需要多 Agent 2. 前置准备 3. 5 个 Agent

By Ne0inhk
【Linux】screen 命令创建会话 保持后台运行

【Linux】screen 命令创建会话 保持后台运行

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕Linux这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获! 文章目录 * Linux 下 screen 命令创建会话保持后台运行 🖥️ * 什么是 screen 命令?🧭 * screen 的核心特性 * screen 的工作原理 * screen 命令的安装与基本使用 🛠️ * 安装 screen * Ubuntu/Debian 系统: * CentOS/RHEL/Fedora 系统: * Arch Linux 系统: * 启动 screen 会话 * 基本操作界面 * 退出 screen 会话 * 方法一:使用快捷键 * 方法二:使用命令 * screen 会话的创建与管理

By Ne0inhk
HarmonyOS应用开发实战(基础篇)Day06-《常见组件》

HarmonyOS应用开发实战(基础篇)Day06-《常见组件》

常见组件 * Image(图像组件) * 基本用法 * 核心属性 * 使用场景 * 注意事项 * Text(文本组件) * 基本用法 * 核心特性 * 使用场景 * 最佳实践 * TextInput(输入框组件) * 基本用法 * 输入类型(`type` 属性) * 密码输入示例 * 其他重要属性 * 安全建议 * Button(按钮组件) * 基本用法 * 类型与样式 * 交互增强 * 使用场景 * Swiper(轮播组件) * 基本结构 * 核心属性 * 注意事项 * List(列表组件) * 基本用法 * 关键特性 * 性能优化 * 验证要点 * Scroll(滚动容器) * 使用条件 * 嵌套限制 * 行为验证 * 总结 Image(图像组件) Image 是 ArkUI

By Ne0inhk
【openclaw+imessage】【免费无限流量】集成方案,支持iphone手机+macos

【openclaw+imessage】【免费无限流量】集成方案,支持iphone手机+macos

🚀 手把手打通 iMessage 私有 API:用 BlueBubbles + OpenClaw 构建本地 AI 短信中枢(技术深度解析) ⚠️ 警告:本文涉及 macOS 私有 API 调用与本地代理配置,属于高阶操作。苹果已明确表示将在 2026 年 6 月 终止此类非合规应用支持,请抓紧窗口期体验。 🔥 核心目标 通过 BlueBubbles Server v1.9.9 调用 macOS 原生 Messages.app 的私有接口,结合 OpenClaw 实现 AI 自动收发 iMessage——无需越狱、无需公网 IP,纯局域网闭环运行,却能通过IMeesage安全地让移动设备和openclaw通讯!通信通道采用苹果加密的IMessage,

By Ne0inhk