【C++初阶】--- vector容器功能模拟实现

1.什么是vector?

在 C++ 里,std::vector 是标准模板库(STL)提供的一个非常实用的容器类,它可以看作是动态数组
在这里插入图片描述

2.成员变量

iterator _start;:指向 vector 中第一个元素的指针。
iterator _finish;:指向 vector 中最后一个元素的下一个位置的指针。
iterator _end_of_storage;:指向 vector 所分配内存空间的末尾的指针。

template<class T>
class vector
{
public:typedef T* iterator;typedef const T* const_iterator;
private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;
}

3.构造函数

3.1默认构造

因为三个成员变量都有缺省值,我们只需要显示写默认构造即可,他们会在初始化链表处初始化。

//写法1:
vector()
{}
//写法2:
vector() = default;//C++11支持强制生成默认构造

3.2拷贝构造

  1. 调用 reserve 函数,提前为新 vector 对象分配足够的内存空间,其大小和 v 的元素数量相同,此步骤可以避免后续添加元素时的频繁扩容。
  2. 借助范围 for 循环遍历 v 中的每个元素,使用引用 auto& 避免不必要的拷贝(特别是当元素为自定义类型时)
  3. 对 v 中的每个元素,调用 push_back 函数将其添加到新 vector 对象的末尾。
//拷贝构造
vector(const vector<T>& v)
{//提前开好空间reserve(v.size());//如果元素是自定义类型,用引用可以减少拷贝for (auto& ch : v){//尾插数据push_back(ch);}
}

3.3迭代器构造函数

迭代器构造函数需要我们传两个迭代器,我们使用范围for将元素尾插至新构造的vector对象中,构造的范围是[first,last)
注意:类模板的成员函数,也可以是函数模板,我们可以将这个迭代器构造函数写成模板,优点是可以用不同的类构造vector对象,前提是元素类型需相同,如:int

//迭代器构造
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);first++;}
}

3.4半缺省构造函数

我们可以通过半缺省构造函数构造一个有n个val值得vector对象

  1. 使用resver提前开好空间,避免后续插入元素时频繁扩容
  2. 使用for循环尾插n个值为val的元素,如果不传参使用的是缺省值T()
  3. 对于内置类型(如 int、double、char 等),T() 会进行值初始化。对于数值类型,会初始化为 0;对于指针类型,会初始化为 nullptr。
  4. 对于自定义类型,T() 会调用该类型的默认构造函数。如果没有显式定义默认构造函数,编译器会自动生成一个(前提是该类没有其他带参数的构造函数

注意:我们使用半缺省构造函数的时候传参需要注意,如下:如果不显示第一个参数是size_t,编译器会优先调用更符合的构造函数,即会上面的迭代器构造函数

//u表示10是size_t类型
vector<int> v1(10u,1);
//用n个val进行构造,T()是匿名对象
vector(size_t n, const T& val = T())
{reserve(n);for (size_t i = 0; i < n; i++){//如果用这种,_finish记得++//*_finish = val;//_finish++;push_back(val);}
}

4.析构函数

三个指针指向同一块空间的不同位置,使用delete释放的时候我们需要使用指向vector中第一个元素的指针,同一块空间不能进行多次释放。

//析构
~vector()
{if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}
}

5.iterators

5.1begin()/end()

//普通对象使用
iterator begin()
{return _start;
}iterator end()
{return _finish;
}//const对象使用
const_iterator begin() const
{return _start;
}const_iterator end() const
{return _finish;
}

6.内联函数

6.1size()

size_t size() const
{return _finish - _start;
}

6.2capacity()

size_t capacity() const
{return _end_of_storage - _start;
}

6.3empty()

bool empty() const
{return _start == _finish;
}

6.4clear()

void clear()
{_finish = _start;
}

6.5swap()

void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);
}

6.7=运算符重载

普通写法:

  1. 先清除当前vector对象中的所有元素
  2. 提前开头看见,避免后续频繁扩容
  3. 使用范围for依次将元素尾插至当前vector对象
//赋值运算符重载
vector<T>& operator=(const vector<T>& v)
{if (this != &v){//先清理原先数据clear();//提前开好空间reserve(v.size());//如果数据是自定义类型可以减少拷贝for (auto& ch : v){push_back(ch);}}return *this;
}

现代写法:

//赋值运算符重载,现代写法
//这里是拷贝,不是引用,且不加const
vector<T>& operator=(vector<T> v)
{swap(v);return *this;
}

6.8[]运算符重载

//普通对象使用
T& operator[](size_t i)
{//i不能越界assert(i < size());return _start[i];
}//const对象使用
const T& operator[](size_t i) const
{//i不能越界assert(i < size());return _start[i];
}

7.尾插/尾删元素

7.1push_back()

  1. 判断空间是否已满,已满需进行扩容操作,使用三木操作符,如果当前vector对象空间对空,扩容至4个元素,不为空则选择2倍扩容。
  2. 尾插元素,_finish指针向后移动一位
//尾插
//如果是自定义类型,用引用可以减少拷贝
void push_back(const T& x)
{//扩容if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finish = x;_finish++;
}

7.2pop_back()

  1. 判断当前vector对象中是否还剩余元素,元素为0则断言失败
  2. 还有元素则将_finish指针前移一位
//尾删
void pop_back()
{//判空assert(!empty());_finish--;
}

8.在pos位置插入删除元素

8.1insert()

  1. 判断pos位置的有效性,可插入范围为[_start,_finish]
  2. 判断空间是否足够,不够则进行扩容操作
    注意:如果进行扩容操作,则会造成迭代器失效,需要更新下迭代器
  3. 将pos位置及之后的数据向后移动一位
  4. 在pos位置插入元素,返回pos位置的迭代器
//在pos位置插入数据
iterator insert(iterator pos, const T& x)
{//pos有效性assert(pos >= _start && pos <= _finish);//扩容if (_finish == _end_of_storage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : 2 * capacity());//扩容后pos迭代器会失效,需要更新一下pos = _start + len;}//将pos及之后的数据往后移动一位iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;
}

8.2erase()

  1. 判断pos位置的有效性,可删除范围为[_start,_finish)
  2. 将pos之后的数据向前移动一位,将pos位置的数据进行覆盖
  3. 别忘了将_finish指针也前移一位
//删除pos位置的数据
template<class T>
void vector<T>::erase(iterator pos)
{//pos有效性assert(pos >= _start && pos < _finish);//向前移动数据iterator it = pos + 1;while (it != end()){*(it - 1) = *it;it++;}--_finish;
}

9.空间大小和数据大小调整

9.1resize()

将vector对象中的数据个数调整至n个,如果n小于当前元素个数,将当前元素个数调整为n,如果n大于当前元素个数,使用val对其进行填充。

//调整长度
template<class T>
void vector<T>::resize(size_t n, T val)
{//n小于长度,缩减长度if (n < size()){_finish = _start + n;}//n大于长度,在后面补元素else{//可能需要扩容reserve(n);//从原数据结尾开始补while (_finish < _start + n){//*_finish = val;//_finish++;push_back(val);}}
}

9.2reserve()

将空间大小扩容至n,如果n小于当前空间大小,不进行操作和改变

  1. 保存旧空间大小old_size,否则后续:
    _start = tmp;
    _finish = tmp + size();
    而size()是_finish-_start,从而_finish = tmp + size()=_start+_finish-_start=_finish
  2. new新空间,大小为n
  3. 将原对象内容进行深拷贝,不能使用memcpy进行拷贝
    在这里插入图片描述
  4. 释放原空间,更新成员变量
template<class T>
void vector<T>::reserve(size_t n)
{//n大于空间大小才扩容if (n > capacity()){size_t old_size = size();//new个新空间T* tmp = new T[n];//memcpy对于内置类型是深拷贝,对于自定义类型是浅拷贝,所以不能用memcpy//memcpy(tmp, _start, size() * sizeof(T));for (size_t i = 0; i < size(); i++){tmp[i] = _start[i];}//销毁旧空间数据delete[] _start;_start = tmp;_finish = tmp + old_size;_end_of_storage = tmp + n;}
}

10打印vector对象内容

C++规定:没有实例化的类模板里取东西,编译器不能区分这里的const_vector是类型还是变量,在前面加上typename表示取的是类型。
当然也可以使用auto自动识别类型。

//打印顺序表的模板
template<class T>
void print_vector(const vector<T>& v)
{//auto it = v.begin();typename vector<T>::const_iterator it = v.begin();while (it != v.end()){cout << *it << " ";it++;}cout << endl;/*	for (auto ch : v){cout << ch << " ";}cout << endl;*/
}

通用打印模板:

//通用打印模板
template<class Container>
void print_vector(const Container& v)
{auto it = v.begin();while (it != v.end()){cout << *it << " ";it++;}cout << endl;//或者//for (auto ch : v)//{//	cout << ch << " ";//}//cout << endl;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/76538.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分布式锁在秒杀场景中的Python实现与CAP权衡

目录 一、分布式锁的前世今生 二、秒杀系统的 “硬核” 挑战 三、Python 实现分布式锁的 “实战演练” Redis 实现:快准狠 ZooKeeper 实现:稳如老狗 数据库实现:老实本分 四、CAP 理论的 “三角恋” 五、性能优化的 “锦囊妙计” 锁粒度控制:粗细有道 超时机制:别…

企业级开发SpringBoost玩转Elasticsearch

案例 Spring Boot 提供了 spring-data-elasticsearch 模块&#xff0c;可以方便地集成 Elasticsearch。 下面我们将详细讲解如何在 Spring Boot 中使用 Elasticsearch 8&#xff0c;并提供示例代码。 1. 添加依赖: 首先&#xff0c;需要在 pom.xml 文件中添加 spring-data-e…

磐石云智能语音客服系统——技术革新引领服务新体验

在人工智能技术飞速发展的今天&#xff0c;企业对于智能化客户服务的需求日益增长。磐石云智能语音客服系统凭借其前沿技术架构与深度场景适配能力&#xff0c;正在重新定义人机交互的边界。本文将深入解析该系统如何通过技术创新实现服务效率与体验的双重突破。 一、意图识别…

OpenGL学习笔记(assimp封装、深度测试、模板测试)

目录 模型加载Assimp网格模型及导入 深度测试深度值精度深度缓冲的可视化深度冲突 模板测试物体轮廓 GitHub主页&#xff1a;https://github.com/sdpyy1 OpenGL学习仓库:https://github.com/sdpyy1/CppLearn/tree/main/OpenGLtree/main/OpenGL):https://github.com/sdpyy1/CppL…

通过AWS EKS 生成并部署容器化应用

今天给大家分享一个实战例子&#xff0c;如何在EKS上创建容器化应用并通过ALB来发布。先介绍一下几个基本概念&#xff1a; IAM, OpenID Connect (OIDC) 2014 年&#xff0c;AWS Identity and Access Management 增加了使用 OpenID Connect (OIDC) 的联合身份支持。此功能允许…

入侵检测snort功能概述

1. 数据包嗅探与日志记录 网络流量监控&#xff1a;实时捕获和分析网络数据包&#xff08;支持以太网、无线等&#xff09;。 日志记录&#xff1a;将数据包以二进制格式&#xff08;pcap&#xff09;或文本格式存储&#xff0c;供后续分析。 2. 协议分析与解码 深度协议解析…

【Easylive】定时任务-每日数据统计和临时文件清理

【Easylive】项目常见问题解答&#xff08;自用&持续更新中…&#xff09; 汇总版 这个定时任务系统主要包含两个核心功能&#xff1a;每日数据统计和临时文件清理。下面我将详细解析这两个定时任务的实现逻辑和技术要点&#xff1a; Component Slf4j public class SysTas…

蓝桥杯 15g

班级活动 问题描述 小明的老师准备组织一次班级活动。班上一共有 nn 名 (nn 为偶数) 同学&#xff0c;老师想把所有的同学进行分组&#xff0c;每两名同学一组。为了公平&#xff0c;老师给每名同学随机分配了一个 nn 以内的正整数作为 idid&#xff0c;第 ii 名同学的 idid 为…

如何使用AI辅助开发R语言

R语言是一种用于统计计算和图形生成的编程语言和软件环境&#xff0c;很多学术研究和数据分析的科学家和统计学家更青睐于它。但对与没有编程基础的初学者而言&#xff0c;R语言也是有一定使用难度的。不过现在有了通义灵码辅助编写R语言代码&#xff0c;我们完全可以用自然语言…

CISCO组建RIP V2路由网络

1.实验准备&#xff1a; 2.具体配置&#xff1a; 2.1根据分配好的IP地址配置静态IP&#xff1a; 2.1.1PC配置&#xff1a; PC0&#xff1a; PC1&#xff1a; PC2&#xff1a; 2.1.2路由器配置&#xff1a; R0&#xff1a; Router>en Router#conf t Enter configuration…

React + TipTap 富文本编辑器 实现消息列表展示,类似Slack,Deepseek等对话框功能

经过几天折腾再折腾&#xff0c;弄出来了&#xff0c;弄出来了&#xff01;&#xff01;&#xff01; 消息展示 在位编辑功能。 两个tiptap实例1个用来展示 消息列表&#xff0c;一个用来在位编辑消息。 tiptap灵活富文本编辑器&#xff0c;拓展性太好了!!! !!! 关键点&#x…

Ubuntu搭建Pytorch环境

Ubuntu搭建Pytorch环境 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 Ubuntu搭建Pytorch环境前言一、Anaconda二、Cuda1.安装流程2、环境变量&#…

Sping Cloud配置和注册中心

1.Nacos实现原理了解吗&#xff1f; Nacos是注册中心&#xff0c;主要是帮助我们管理服务列表。Nacos的实现原理大概可以从下面三个方面来讲&#xff1a; 服务注册与发现&#xff1a;当一个服务实例启动时&#xff0c;它会向Nacos Server发送注册请求&#xff0c;将自己的信息…

C++笔记之父类引用是否可以访问到子类特有的属性?

C++笔记之父类引用是否可以访问到子类特有的属性? code review! 参考笔记 1.C++笔记之在基类和派生类之间进行类型转换的所有方法 文章目录 C++笔记之父类引用是否可以访问到子类特有的属性?1.主要原因2.示例代码3.说明4.如何访问子类特有的属性5.注意事项6.总结在 C++ 中,…

JavaScript逆向工程:如何判断对称加密与非对称加密

在现代Web应用安全分析中&#xff0c;加密算法的识别是JavaScript逆向工程的关键环节。本文将详细介绍如何在逆向工程中判断JavaScript代码使用的是对称加密还是非对称加密。 一、加密算法基础概念 1. 对称加密 (Symmetric Encryption) 特点&#xff1a;加密和解密使用相同的…

物理备份工具 BRM vs gs_probackup

什么是BRM 上一篇文章讲了openGauss的物理备份工具gs_probackup&#xff0c;今天来说说BRM备份工具。 BRM备份恢复工具全称为&#xff1a;Backup and Recovery Manager&#xff0c;是MogDB基于opengauss的备份工具 gs_probackup 做了一些封装和优化,面向MogDB数据库实现备份和…

问问lua怎么写DeepSeek,,,,,

很坦白说&#xff0c;这十年&#xff0c;我几乎没办法从互联网找到这个这样的代码&#xff0c;互联网引擎找不到&#xff0c;我也没有很大的“追求”要传承&#xff0c;或者要宣传什么&#xff1b;直到DeepSeek的出现 兄弟&#xff0c;Deepseek现在已经比你更了解你楼下的超市…

react+Tesseract.js实现前端拍照获取/选择文件等文字识别OCR

需求背景 在开发过程中可能会存在用户上传一张图片后下方需要自己识别出来文字数字等信息&#xff0c;有的时候会通过后端来识别后返回&#xff0c;但是也会存在纯前端去识别的情况&#xff0c;这个时候就需要使用到Tesseract.js这个库了 附Tesseract.js官方&#xff08;htt…

蓝桥杯考前复盘

明天就是考试了&#xff0c;适当的停下刷题的步伐。 静静回望、思考、总结一下&#xff0c;我走过的步伐。 考试不是结束&#xff0c;他只是检测这一段时间学习成果的工具。 该继续走的路&#xff0c;还是要继续走的。 只是最近&#xff0c;我偶尔会感到迷惘&#xff0c;看…

前端-Vue3

1. Vue3简介 2020年9月18日&#xff0c;Vue.js发布版3.0版本&#xff0c;代号&#xff1a;One Piece&#xff08;n 经历了&#xff1a;4800次提交、40个RFC、600次PR、300贡献者 官方发版地址&#xff1a;Release v3.0.0 One Piece vuejs/core 截止2023年10月&#xff0c;最…