探索C嘎嘎的奇妙世界:第十七关---STL(vector的模拟实现)

        vector是一种动态数组,可以动态调整大小并按照索引访问元素。由于很多接口在string中都有所重复,所以这次来讲一些有所区别的接口

1. 迭代器

        Vector中的迭代器是一种用于遍历vector中元素的对象。迭代器提供了一种访问vector中元素的统一方式,使得可以通过一种通用的语法来遍历和访问容器中的元素。在C++中,vector的迭代器是一个类对象,它包含了指向vector中元素的指针,并提供了一系列操作符和方法来访问和操作元素。通过使用迭代器,可以方便地遍历vector中的元素,进行查找、插入、删除等操作。同时,迭代器也提供了一种与算法和其他容器进行交互的统一接口,使得代码更加灵活和可扩展。请看示例代码:

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

        上述我们实现了两种迭代器,一种是const的一种是非const,用来适应不同的场景,让我们一起来解析一下上述代码吧:

        首先,通过typedef关键字将T类型定义为iterator类型,将const T类型定义为const_iterator类型。这样可以方便地使用iterator和const_iterator来声明并操作迭代器。

        接着,定义了begin()和end()方法,其中const_iterator版本是用于const对象的,iterator版本是用于非const对象的。

        在const_iterator版本的begin()方法中,直接返回指向vector第一个元素的指针_start。

        在const_iterator版本的end()方法中,直接返回指向vector最后一个元素后面一个位置的指针_finish。

        在iterator版本的begin()方法中,同样返回指向vector第一个元素的指针_start,不同的是这是一个非const版本的指针,可以用于修改vector中的元素。

        在iterator版本的end()方法中,同样返回指向vector最后一个元素后面一个位置的指针_finish,也是一个非const版本的指针。

        通过实现这些方法,可以实现在vector对象上使用迭代器进行遍历和访问元素的功能。同时,const_iterator版本的方法可以保证在const对象上也可以使用迭代器进行遍历,但不能对元素进行修改。而iterator版本的方法可以方便地在非const对象上进行修改操作。

2. 拷贝构造函数、赋值构造函数以及析构函数

        对于这些函数,想必我们轻车熟路了吧,那就不过多介绍了,请看示例代码:

		template <class InputIterator>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);}}vector(initializer_list<T> il){reserve(il.size());for (auto e : il){push_back(e);}}vector(int n, const T& val = T()){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}// 强制编译器生成默认的vector() = default;// v2(v1)vector(const vector<T>& v){reserve(v.capacity());for (auto e : v){push_back(e);}}

让我们来分析一下:

        1. 参数为两个迭代器first和last的构造函数:使用了模板技术,接收一个表示范围的[first, last)迭代器,并通过循环将范围内的元素逐个调用push_back()函数插入到vector中。

        2. 参数为元素个数n和初始值val的构造函数:通过reserve()函数预留空间,然后使用循环调用push_back()函数将n个val元素插入到vector中。

        3. 使用initializer_list<T>参数的构造函数:通过reserve()函数预留空间,然后使用循环遍历initializer_list并调用push_back()函数将其中的元素插入到vector中。

        4. 参数为整数n和初始值val的构造函数:与第2个构造函数类似,通过reserve()函数预留空间,然后使用循环调用push_back()函数将n个val元素插入到vector中。

        5. 默认构造函数:使用了默认的函数定义,没有做任何处理。

        6. 拷贝构造函数:接收一个vector对象v作为参数,通过reserve()函数预留空间,然后使用循环遍历v并调用push_back()函数将其中的元素逐个插入到新的vector中。

        通过这些构造函数的实现,可以实现多种不同的初始化vector的方式,例如可以从迭代器范围、指定元素个数和初始值、initializer_list等不同的数据源构造vector对象。

3. swap、capacity、size以及operator[ ]的重载

请看示例代码:

void swap(vector<T>&v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);
}size_t capacity()
{return _end_of_storage - _start;
}size_t size()
{return _finish - _start;
}T& operator[](size_t i)
{assert(i >= 0 && i < size());return *(_start + i);
}const T& operator[](size_t i) const
{assert(i < size());return _start[i];
}

上述代码是vector类中的一些成员函数的实现。

        1. swap函数:用于交换两个vector对象的数据。通过调用std::swap()函数,分别交换_start、_finish和_end_of_storage这三个成员变量的值。

        2. capacity函数:返回vector当前能够容纳的元素个数,即_end_of_storage减去_start的值。

        3. size函数:返回vector当前实际存储的元素个数,即_finish减去_start的值。

        4. operator[]函数:重载了下标运算符,通过下标i来访问vector中的元素。在operator[]函数中使用了断言判断下标是否合法,然后使用指针算术运算来找到指定位置的元素并返回。其中,非const版本返回的是引用,可以进行修改;const版本返回的是常量引用,只能进行读取操作。

        这些函数的实现为vector类提供了一些常用的功能,如交换两个vector对象、获取当前容量和大小、以及通过下标访问元素等。

4. reserve、push_back以及pop_back

请看示例代码:

		void reserve(size_t n){if (n > capacity()){size_t oldsize = size();iterator tmp = new T[n];if (_start){//memcpy(tmp, _start, sizeof(T) * size());//浅拷贝for (size_t i = 0; i < size(); i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + oldsize;_end_of_storage = _start + n;}}void push_back(const T& x){if (_finish == _end_of_storage){size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);}*_finish = x;_finish++;}void pop_back(){assert(size() > 0);_finish--;}

上述代码是vector类中的reserve、push_back和pop_back函数的实现。

        1. reserve函数:用于预分配内存空间以容纳至少n个元素。首先判断n是否大于当前的容量,如果是,则需要重新分配内存。首先保存当前的大小为oldsize,然后创建一个临时指针tmp,分配大小为n的新的内存空间。如果_start指针不为空,则将原始元素逐个复制到新的内存空间中,注意这里是深拷贝。然后释放原始内存空间,并更新_start、_finish和_end_of_storage的值。

        2. push_back函数:用于在vector尾部添加一个元素x。如果当前的空间不够容纳新的元素,则调用reserve函数进行内存扩容。然后将新元素x赋值给_finish指向的位置,并将_finish指针向后移动一位。

        3. pop_back函数:用于删除vector尾部的一个元素。该函数首先使用断言判断vector是否为空,然后将_finish指针向前移动一位,即将最后一个元素的位置腾空。

        这些函数实现了向vector中添加和删除元素的功能,以及在需要时进行内存扩容,保证了vector的动态增长性能。

5. insert以及erase

请看示例代码:

		void insert(iterator pos, const T& x){assert(pos >= _start && pos <= _finish);if (_finish == _end_of_storage){size_t len = pos-_start;size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);pos = _start + len;}iterator it = _finish-1;while (it >= pos){*(it + 1) = *it;it--;}*pos = x;_finish++;}void erase(iterator pos){assert(pos >= _start && pos <= _finish);iterator it1 = pos + 1;while (it1 < _finish){*(it1 - 1) = *it1;it1++;}_finish--;}

上述代码是vector类中的insert和erase函数的实现。

        1. insert函数:用于在指定的位置pos插入一个元素x。首先使用断言判断pos的合法性,即pos必须在_start和_finish之间。然后判断当前的空间是否足够容纳新的元素,如果不够,则调用reserve函数进行内存扩容,并更新pos的位置。然后将pos之后的元素逐个向后移动一位,给新元素腾出位置。最后将新元素x赋值给pos指向的位置,并将_finish指针向后移动一位。

        2. erase函数:用于删除指定位置pos的元素。首先使用断言判断pos的合法性,即pos必须在_start和_finish之间。然后将pos之后的元素逐个向前移动一位,覆盖掉pos指向的元素。最后将_finish指针向前移动一位,即删除了pos位置的元素。

        这些函数实现了在vector中插入和删除元素的功能,通过移动和覆盖元素的方式实现了元素的插入和删除操作。

         到此我们只是简单的模拟实现了一下STL中vector的相关接口~,后续我们会一一展开学习的,希望这篇博客能给您带来一些启发和思考!那我们下次再一起探险喽,欢迎在评论区进行讨论~~~

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

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

相关文章

vue2.7支持组合式API,但是对应的vue-router3并不支持useRoute、useRouter。

最近在做一个项目&#xff0c;因为目标用户浏览器版本并不确定&#xff0c;可能会有较旧版本&#xff0c;于是采用vue2.7而不是vue3&#xff0c;最近一年多使用vue3开发的项目都碰到了很多chrome 63-73版本&#xff0c;而对应UI 库 element plus又问题很多。 为了不碰到这些问…

TCP 协议详解:三次握手与四次挥手

在网络通信中&#xff0c;确保数据准确无误地传递是至关重要的。TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;作为一种面向连接的、可靠的、基于字节流的通信协议&#xff0c;在网络数据传输中起到了核心作用。本文将详细解析 TCP 的基本…

神经网络学习5-非线性激活

非线性激活&#xff0c;即 这是最常用的 inplaceTrue 原位操作 改变变量本身的值&#xff0c;就是是否输入时若原本有值&#xff0c;是否更换 该函数就是表示&#xff1a;输入小于零时输出0&#xff0c;大于零时保持不变 代码如下&#xff1a; import torch from torch imp…

Python初体验

# Java基础知识学的差不多了&#xff0c;项目上又没什么事&#xff0c;学学py&#xff0c;方便以后对接 1、打包flask应用&#xff08;好痛苦&#xff0c;在什么平台打包就只在那个平台可用想在linux用只能参考方法2了&#xff09; pyinstaller --onefile app.py -n myapp 2…

Databend 开源周报第 149 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 支持递归公共表…

(项目实战)RocketMQ5.0延迟消息在聚合支付系统中的应用

1 基于业务场景掌握RocketMQ5.0 本篇文章主要结合聚合支付系统中的业务场景来落地RocketMQ中间件的应用&#xff0c;聚合支付系统主要在支付系统超时订单和商户支付结果异步通知场景中会使用到RocketMQ消息中间件。本文使用到了RocketMQ中的延迟消息知识点&#xff0c;RocketM…

数据库精选题(一)(关系数据库设计)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;数据库 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 前言 练习题 题型一&#xff1a;判断关系…

百度地图使用任意图片旋转任意角度作为地面贴图

公司项目有个需求是要在地图上贴个航拍的照片做出类似卫星地图的效果,但是只有一张图片而且可以随时替换,也不好做瓦片地图,而且照片的角度可以任意旋转。 要实现这个功能需要解决以下问题: 百度地图怎么贴图片图片角度如何旋转 不卖关子,我先放出实现的效果,为了不涉及侵…

ansible file模块

file模块 管理文件和目录的属性&#xff0c;如状态&#xff08;是否存在&#xff09;、权限、所有权等。 1、创建文件&#xff1a; 使用file模块的state参数设置为touch可以创建文件。例如&#xff1a; ansible 组名 -m file -a "path/csdn/jingyu statetouch" …

期末考后怎样发成绩?

老师们&#xff0c;下周可就是期末考啦&#xff0c;又到了头疼发成绩的时候了。每当这个时候&#xff0c;家长们总是急切地咨询孩子的考试表现&#xff0c;向老师们询问成绩。这种场景几乎成了每学期结束时的常态。 别担心&#xff0c;我来安利一个超棒的工具——“易查分小程序…

Java开发接口设计的原则

在现代软件开发实践中&#xff0c;接口设计扮演着至关重要的角色。它不仅关乎代码的结构和未来的可维护性&#xff0c;还直接影响到软件系统的灵活性和扩展性。本文将通过实例详解几个核心的接口设计原则&#xff0c;帮助开发者更好地编写和管理接口&#xff0c;从而提升软件的…

一键制作,打造高质量的数字刊物

随着数字化时代的到来&#xff0c;数字刊物已经成为信息传播的重要载体。它以便捷、环保、互动性强等特点&#xff0c;受到了越来越多人的青睐。然而&#xff0c;如何快速、高效地制作出高质量的数字刊物&#xff0c;成为许多创作者面临的难题。今天&#xff0c;教大家一个制作…

【golang学习之旅】Go中的变量(1)

系列文章 【golang学习之旅】使用VScode安装配置Go开发环境 【golang学习之旅】报错&#xff1a;a declared but not used 【golang学习之旅】Go 的基本数据类型 【golang学习之旅】深入理解字符串string数据类型 【golang学习之旅】go mod tidy 【golang学习之旅】记录一次 p…

Codepen Three.js环境依赖配置

Codepen Three.js环境依赖配置 前言 如果想在CodePen环境写Three.js依赖的项目&#xff0c;环境搭建可以参考该Codepen项目: Chill the lion 详细 打开设置可以看到以下配置 更多项目参考 1. Chill the Lion Chill the Lion 是一个基于 ThreeJS 制作的 WebGL 示例。它由…

使用MyBatis的动态SQL注解实现实体的CRUD操作

使用MyBatis的动态SQL注解实现实体的CRUD操作 1. 引言2. 准备工作2.1 创建数据表2.2 创建实体类 Book2.3 修改Mapper接口 BookMapper 3. 测试CRUD操作3.1 配置日志3.2 查询操作3.3 新增操作3.4 修改操作3.5 删除操作 4. 与MyBatis Plus的对比5. 动态SQL注解的适用场景5.1 动态查…

AcWing算法基础课笔记——质数

质数 质数&#xff1a;在大于1的整数中&#xff0c;如果只包含1和它本身这两个约数&#xff0c;就被称之为质数 &#xff08;1&#xff09;质数的判定——试除法 bool is_prime(int n) {if (n < 2) return false;for(int i 2; i < n / i; i ) {if(n % i 0) return …

API低代码平台介绍6-数据库记录删除功能

数据库记录删除功能 在前续文章中我们介绍了如何插入和修改数据库记录&#xff0c;本篇文章会沿用之前的测试数据&#xff0c;介绍如何使用ADI平台定义一个删除目标数据库记录的接口&#xff0c;包括 单主键单表删除、复合主键单表删除、多表删除&#xff08;整合前两者&#x…

JS 【详解】树的遍历(含深度优先遍历和广度优先遍历的算法实现)

用 js 描述树 let tree [{label:a,children:[{label:b,children:[{label:d},{label:e}]},{label:c,children:[{label:f}]}]} ]使用数组是因为树的节点有顺序 深度优先遍历 从根节点出发&#xff0c;优先遍历最深的节点 遍历顺序为 abdecf function DFS(tree) {tree.forEach(…

揭示SOCKS5代理服务器列表的重要性

在复杂的网络安全领域中&#xff0c;SOCKS5代理在保护在线活动方面发挥着关键作用。本文深入探讨了SOCKS5代理服务器列表的细节&#xff0c;探讨了它们的应用、优势以及在增强在线安全和隐私方面不可或缺的功能。 一、理解SOCKS5代理服务器列表 作为在客户端和服务器之间进行通…

QListWidget、QMenu、Action、customContextMenuRequested

QListWidget的初始化、清空、Append添加、Insert添加、删除item QListWidget的事件的使用 QToolBox的使用&#xff0c;每个Page可以添加其他控件 QToolBar使用代码添加QMenu,QMenu添加3个Action QToolButton绑定Action 布局 其中 QSplitter比较特殊&#xff0c; 允许在水平或垂…