C++初阶:适合新手的手撕vector(模拟实现vector)

上次讲了常用的接口:C++初阶:容器(Containers)vector常用接口详解
今天就来进行模拟实现啦


文章目录

  • 1.基本结构与文件规划
  • 2.空参构造函数(constructor)
  • 4.基本函数(size(),capacity(),resize(),reserve())
  • 4.增删改查(push_back,pop_back,insert,erase)
  • 5.在实现Insert和erase时迭代器失效问题
  • 6.重载[]
  • 7. 完善构造函数
    • 7.1vector (size_type n, const value_type& val = value_type());
    • 7.2利用迭代器进行构造
    • 7.3拷贝构造
  • 8.重载=
  • 9.析构函数


1.基本结构与文件规划

请添加图片描述

  • vector.h头文件:包含类的全部(函数的声明与定义)
  • test.cpp源文件:进行调用test函数,测试和完善功能

基本结构,先看一下源码:

请添加图片描述

namespace MyVector
{template <class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;//先定义好迭代器//各种函数private:iterator _start;iterator _finish;iterator _endOfStorage;};
}
  • _start:指向动态数组的起始位置的指针,即第一个元素的位置。
  • _finish:指向动态数组中最后一个元素之后的位置的指针。在这个实现中,_finish 指针始终指向当前元素范围的末尾,也就是下一个要插入元素的位置。
  • _endOfStorage:指向动态数组分配的内存空间的末尾之后的位置的指针。在这个实现中,_endOfStorage 指针指向当前分配的内存空间的末尾,当需要扩充容量时,会通过比较 _finish_endOfStorage 的位置来判断是否需要重新分配更大的内存空间

2.空参构造函数(constructor)

		vector():_start(nullptr), _finish(nullptr), _endOfStorage(nullptr)//直接使用初始化列表{}

都初始化为空指针


#3.迭代器(iterator)(begin(),end())

		iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin()const{return _start;}const_iterator end()const{return _finish;}

进行const的重载

4.基本函数(size(),capacity(),resize(),reserve())

		void reserve(size_t n){if (n > capacity()){int old_size = size();//保存一下长度,方便后续给_finish移到新的位置T* tmp = new T[n];if (_start != nullptr)//vector里存东西了{for (size_t i = 0; i < size(); ++i){tmp[i] = _start[i];//_start本质是指针}}delete[] _start;_start = tmp;_finish = _start + old_size;_endOfStorage = _start + n;}}void resize(size_t n, const T& x = T()){if (n > size()){reserve(n);//<capacity 的话,也没有进行处理while (_finish != _start + n){*_finish = x;++_finish; }}else{_finish = _start + n;//小于长度时,直接移动finish}}size_t size(){return _finish - _start;}size_t capacity(){return _endOfStorage - _start;}
  1. reserve 函数:
  • reserve 函数用于保留至少能容纳 n 个元素的内存空间。如果当前的容量小于 n,则会分配新的内存空间,并将原来的元素复制到新的内存空间中。
  • 首先,它会创建一个新的大小为 n 的临时数组 tmp,然后将原始数组中的元素复制到临时数组中。
  • 接着,释放原始数组的内存空间,将 _start 指针指向新分配的内存空间,同时更新 _finish_endOfStorage 的位置。
  1. resize 函数:
  • resize 函数用于改变数组的大小,使其包含 n 个元素,并使用值 x 进行初始化。
  • 如果 n 大于当前的大小,它会调用 reserve 函数以确保数组有足够的容量,然后将数组的大小增加到 n,并使用值 x 进行初始化。
  • 如果 n 小于当前的大小,它会直接将 _finish 指针移动到新的位置,从而改变数组的大小。
  1. size 函数:
  • size 函数用于返回数组中元素的个数,即 _finish_start 之间的距离。
  1. capacity 函数:
  • capacity 函数用于返回数组的容量,即 _endOfStorage_start 之间的距离

怎么来理解:const T& x = T()

实现给出各种类型的默认值,在这里为了妥协,其实内置类型也有构造函数在 C++ 中。内置类型(如 intfloatdouble 等)也有默认构造函数。默认构造函数对于内置类型来说,其实就是不带参数的构造函数,它会将变量初始化为默认值

  1. T() 表示创建一个类型 T 的临时对象,并进行值初始化。这里假设 T 是一个类或者结构体,那么这个语句会调用 T 的默认构造函数来创建一个临时对象。
  2. const T& x 表示创建一个类型为 T 的常量引用 x。这里的引用是 T 类型的引用,而且是常量引用,意味着 x 引用的对象是不可修改的。
  3. const T& x = T() 将这个临时对象绑定到常量引用 x 上。这样做的好处是可以避免不必要的拷贝,同时也可以确保 x 引用的对象是不可修改的。

使用如下来测试

	void test1(){vector<int> v;for (auto e : v){cout << e << " ";}cout << endl;v.resize(10);for (auto e : v){cout << e << " ";}cout << endl;v.resize(5);for (auto e : v){cout << e << " ";}cout << endl;}

请添加图片描述


4.增删改查(push_back,pop_back,insert,erase)

		void push_back(const T& x){if (_finish == _endOfStorage){int newcapacity = capacity() == 0 ? 2 : 2 * capacity();reserve(newcapacity);}*_finish = x;_finish++;}void pop_back(){assert(size() > 0);--_finish;}iterator insert(iterator pos, const T& x)//在pos前插入{assert(pos < _finish&& pos >= _start);if (_finish == _endOfStorage){size_t site = pos - _start;int newcapacity = capacity() == 0 ? 2 : 2 * (capacity());reserve(newcapacity);pos = _start + site;//pos到新空间的位置上}iterator end = _finish - 1;while (end >= pos)//开始整体向后退{*(end + 1) = *end;end--;}*pos = x;++_finish;return pos;}iterator erase(iterator pos)//删pos处{assert(pos < _finish&& pos >= _start);assert(size() > 0);//开始向前移动iterator start = pos + 1;while (start < _finish){*(start - 1) = *start;start++;}_finish--;return pos;//返回删除的位置}

使用test2函数看功能是否正常

void test2(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);//尾插3个for (auto e : v){cout << e << " ";}cout << endl;v.pop_back();//尾删一个for (auto e : v){cout << e << " ";}cout << endl;v.insert(v.begin(), 0);//头插一个0for (auto e : v){cout << e << " ";}cout << endl;v.erase(v.begin());//头删for (auto e : v){cout << e << " ";}cout << endl;}

请添加图片描述


5.在实现Insert和erase时迭代器失效问题

当使用迭代器遍历容器时,如果在遍历的过程中对容器进行了结构性的修改(例如插入、删除元素,重新分配内存等操作),可能会导致迭代器失效。迭代器失效意味着该迭代器不再指向有效的元素或容器的结尾,因此继续使用失效的迭代器可能会导致未定义行为。

迭代器失效的原因主要有以下几种:

  1. 插入操作:当在容器中插入元素时,可能会导致容器内部的元素发生移动或重新分配内存,这会导致原先的迭代器失效。因为插入元素后,原先的迭代器可能不再指向正确的位置。
  2. 删除操作:当在容器中删除元素时,可能会导致容器内部的元素发生移动,也会导致原先的迭代器失效。因为删除元素后,原先的迭代器可能指向了一个已经被删除的元素,或者指向了不正确的位置。
  3. 重新分配内存(扩容时):某些容器在元素数量达到一定阈值时会进行内存的重新分配,这会导致原先的迭代器失效。因为重新分配内存后,原先的迭代器可能指向了无效的内存地址。
  4. 容器的清空:当对容器进行清空操作时,所有的元素都被移除,迭代器也会失效。

迭代器失效可以大致分为两类:

  1. 结构性变化导致的失效:这类失效包括扩容时申请了新空间、插入或删除元素导致元素位置改变等情况。在这种情况下,原先的迭代器可能会指向已经被移动或者删除的元素,或者指向了新分配的内存空间,导致迭代器失效。
  2. 数据变化导致的失效:这类失效包括使用了 memmovestd::copy 等函数对容器内部元素进行移动或复制的情况。这些函数可能会导致容器内部的元素发生移动,导致原先的迭代器指向的位置发生变化,从而导致迭代器失效。
	void test3(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(6);for (auto e : v){cout << e << " ";}cout << endl;//删除偶数vector<int>::iterator it = v.begin();while (it != v.end()){if (*it % 2 == 0){it=v.erase(it);//这里不能只是v.erase(it); 删除后}else{it++;}}for (auto e : v){cout << e << " ";}cout << endl;}

在使用 erase 函数删除元素后,erase 函数会返回指向被删除元素之后的元素的迭代器,而不是原先被删除元素的迭代器。如果使用 v.erase(it);,则会导致 it 迭代器失效,因为它指向的元素已经被删除,而 it 没有更新。因此,为了确保迭代器的有效性,需要将返回的迭代器赋值给 it,以便在下一次循环中继续使用正确的迭代器。


6.重载[]

		T& operator[](size_t i){assert(i < size());return _start[i];}const T& operator[](size_t i) const{assert(i < size());return _start[i];}

7. 完善构造函数

7.1vector (size_type n, const value_type& val = value_type());

		vector(size_t n, const T& val= T()){resize(n, val);}vector(int n, const T& val = T())//适用于  vector<int> v(5,1){resize(n, val);}

7.2利用迭代器进行构造

		template <class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);first++;}}

为什么使用模版:

因为可能使用其他类型的迭代器来进行初始化

7.3拷贝构造

		vector(const vector<T>& v):_start(nullptr),_finish(nullptr),_endOfStorage(nullptr)//先利用初始化列表进行初始化{reserve(v.capacity());for (const auto& e : v){push_back(e);}}

8.重载=

		void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endOfStorage, v._endOfStorage);}vector<T>& operator=(vector<T> v){swap(v);return *this;}

注意这里的参数不是常量引用,而是按值传递的。这是因为在赋值操作符中我们会调用 swap 函数,按值传递可以保证传入的参数会被复制一份,避免对原对象的修改。在函数体内,我们调用了 swap 函数,将当前对象和传入的对象进行内容交换,然后返回 *this,即当前对象的引用。


9.析构函数

		~vector(){delete[] _start;_start = _finish = _endOfStorage = nullptr;}

好啦,今天就到这里啦,感谢大家支持!!!

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

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

相关文章

算法学习——LeetCode力扣栈与队列篇1

算法学习——LeetCode力扣栈与队列篇1 232. 用栈实现队列 232. 用栈实现队列 - 力扣&#xff08;LeetCode&#xff09; 描述 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQu…

HiveSQL——sum(if()) 条件累加

注&#xff1a;参考文章&#xff1a; HiveSql面试题10--sum(if)统计问题_hive sum if-CSDN博客文章浏览阅读5.8k次&#xff0c;点赞6次&#xff0c;收藏19次。0 需求分析t_order表结构字段名含义oid订单编号uid用户idotime订单时间&#xff08;yyyy-MM-dd&#xff09;oamount订…

阿里云游戏服务器多少钱一年?

阿里云游戏服务器租用价格表&#xff1a;4核16G服务器26元1个月、146元半年&#xff0c;游戏专业服务器8核32G配置90元一个月、271元3个月&#xff0c;阿里云服务器网aliyunfuwuqi.com分享阿里云游戏专用服务器详细配置和精准报价&#xff1a; 阿里云游戏服务器租用价格表 阿…

【计算几何】确定两条连续线段向左转还是向右转

确定两条连续线段向左转还是向右转 目录 一、说明二、算法2.1 两点的叉积2.2 两个段的叉积 三、旋转方向判别3.1 左转3.2、右转3.3 共线判别 一、说明 如果是作图&#xff0c;或者是判别小车轨迹。为了直观地了解&#xff0c;从当前点到下一个点过程中&#xff0c;什么是左转、…

第77讲用户管理功能实现

用户管理功能实现 前端&#xff1a; views/user/index.vue <template><el-card><el-row :gutter"20" class"header"><el-col :span"7"><el-input placeholder"请输入用户昵称..." clearable v-model"…

JavaEE作业-实验二

目录 1 实验内容 2 实验要求 3 思路 4 核心代码 5 实验结果 1 实验内容 实现两个整数求和的WEB程序 2 实验要求 ①采用SpringMVC框架实现 ②数据传送到WEB界面采用JSON方式 3 思路 ①创建一个SpringMVC项目&#xff0c;配置好相关的依赖和配置文件。 ②创建一个Con…

Centos7之忘记Root用户密码的处理方式

Centos7之忘记Root用户密码的处理方式 文章目录 Centos7之忘记Root用户密码的处理方式1.场景描述2. 重置密码1. 重启系统进入编辑界面2. 按方向键下键↓&#xff0c;找到设置语言的地方3. 进入bash界面后&#xff0c;可以输入passwd命令重新设置root密码 1.场景描述 长时间未使…

导数的几何意义【高数笔记】

1. 高数中的导数几何意义&#xff0c;与中学中斜率的联系 2. 导函数与导数的区别和联系又是什么 3. 导数的几何意义的题型是什么 4. 这些题型又有哪些区别 5. 点在曲线外和点在曲线上&#xff0c;需要注意什么 6. 法线和切线有什么关系 7. 法线是什么

无性能损失!让SAM加速近50倍!EfficientViT-SAM来了!

今天给大家分享一个加速视觉分割大模型的工作&#xff1a;EfficientViT-SAM。这是一种新的加速SAM系列。保留了SAM的轻量级提示编码器和mask解码器&#xff0c;同时用EfficientViT替换了沉重的图像编码器。对于训练&#xff0c;首先从SAM-ViT-H图像编码器到EfficientViT的知识蒸…

Github 2024-02-11 开源项目日报Top10

根据Github Trendings的统计&#xff0c;今日(2024-02-11统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目4非开发语言项目2C项目1C项目1Solidity项目1JavaScript项目1Rust项目1HTML项目1 免费服务列表 | f…

实战案例:将已有的 MySQL8.0 单机架构变成主从复制架构

操作步骤 修改 master 主节点 的配置&#xff08; server-id log-bin &#xff09;master 主节点 完全备份&#xff08; mysqldump &#xff09;master 主节点 创建复制用户并授权master 主节点 将完全备份文件拷贝至从节点修改 slave 从节点 的配置&#xff08; server-id rea…

全功能的屏幕截图工具 - PicPick

全功能的屏幕截图工具 - PicPick 1. PicPick1.1. PicPick 中文1.2. Hot keys References 1. PicPick https://picpick.app/zh/ https://picpick.app/en/ A full-featured screen capture and recording tool, Intuitive image editor, color picker, color palette, pixel-ru…

腾讯云4核8G服务器性能如何?支持多少用户访问?

腾讯云4核8G服务器支持多少人在线访问&#xff1f;支持25人同时访问。实际上程序效率不同支持人数在线人数不同&#xff0c;公网带宽也是影响4核8G服务器并发数的一大因素&#xff0c;假设公网带宽太小&#xff0c;流量直接卡在入口&#xff0c;4核8G配置的CPU内存也会造成计算…

【大厂AI课学习笔记】【1.5 AI技术领域】(10)对话系统

对话系统&#xff0c;Dialogue System&#xff0c;也称为会话代理。是一种模拟人类与人交谈的计算机系统&#xff0c;旨在可以与人类形成连贯通顺的对话&#xff0c;通信方式主要有语音/文本/图片&#xff0c;当然也可以手势/触觉等其他方式 一般我们将对话系统&#xff0c;分…

算法学习——LeetCode力扣栈与队列篇2

算法学习——LeetCode力扣栈与队列篇2 150. 逆波兰表达式求值 150. 逆波兰表达式求值 - 力扣&#xff08;LeetCode&#xff09; 描述 给你一个字符串数组 tokens &#xff0c;表示一个根据 逆波兰表示法 表示的算术表达式。 请你计算该表达式。返回一个表示表达式值的整数。…

X图形

1.题目 这道题是蓝桥云课上面的一道题目&#xff0c;它是2022年蓝桥杯省模拟题&#xff0c;题目难度为简单。 考察的知识点为递归。 题目链接&#xff1a;X图形 2.思路 如何理解题意&#xff1f; 蓝桥杯的题目和Leetcode题目最大的不同点在于&#xff0c;蓝桥杯的题目大部…

IDEA中Git的使用小技巧-Toolbar(工具栏)的设置

目录 1 前言 2 步骤 2.1 打开设置 2.2 找到Menus and Toolbars 2.3 Menus and Toolbars界面的介绍 2.4 选择工具 2.5 查看 1 前言 工具栏的合理运用&#xff0c;能够极大程度上为我们省时省力 &#xff0c;接下来我将以Git工具的添加&#xff0c;介绍如何定制我们IDEA…

Makefile编译原理 make 中的路径搜索_1

一.make中的路径搜索 问题&#xff1a;在实际的工程项目中&#xff0c;所有的源文件和头文件都放在同一个文件夹中吗&#xff1f; 实验1 &#xff1a; VPATH 引子 mhrubuntu:~/work/makefile1/17$ ll total 28 drwxrwxr-x 4 mhr mhr 4096 Apr 22 00:46 ./ drwxrwxr-x 7 mhr m…

离开亚马逊7.5年后的真心话

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

【开源】SpringBoot框架开发厦门旅游电子商务预订系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 景点类型模块2.2 景点档案模块2.3 酒店管理模块2.4 美食管理模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 学生表3.2.2 学生表3.2.3 学生表3.2.4 学生表 四、系统展示五、核心代码5.1 新增景点类型5.2 查询推荐的…