STL?vector!!!

一、前言

        之前我们借助手撕string加深了类和对象相关知识,今天我们将一起手撕一个vector,继续深化类和对象、动态内存管理、模板的相关知识

二、vector相关的前置知识

        1、什么是vector?


        vector是一个STL库中提供的类模板,它是存储元素对象的顺序表,其中提供了一些有关增删查改的接口,它的特点是可以通过下标的方式在表中的任意位置进行读、写

        2、vector中的相关接口

        在本文接下来的部分会介绍vector的常用接口,事实上借助这些接口就可以解决平常所能遇到的大部分问题,如果还需要了解vector提供的更多接口及使用方法的话,可以跳转到一下网页:
        vector - C++ Referencehttps://legacy.cplusplus.com/reference/vector/vector/?kw=vector

三、手撕一个vector类

        1、成员变量与整体框架

        注意:之前的顺序表我们都是通过记录指针、元素个数和空间大小来完成的,顺序表的实现自然也可以这样,但是STL库中不是这样实现的,所以今天我们学习一下STL库中的实现方式

        

        上面的_start、_finish、_endofstorge分别代表着指向有效元素起始位置、终止位置的下一个位置和空间终止位置的迭代器,所以在上面我们已经定义了迭代器,事实上就是指针类型,这是由于顺序表的一大特点就是在内存中连续存储,所以可以直接使用未封装的指针;另一方面,我们在声明成员变量的同时都给了一个缺省值,这是由于它们都是指针类型,在初始化时我们都要先初始化成空指针再进行下一步操作,所以我们可以直接在这里给一个缺省值,这样避免了在初始化列表多次的显示初始化成空指针

        2、构造函数

        库中的函数头:
        

        以上的三个构造函数我们都会一一实现,在这里补充一点:在上面的构造函数中出现了"clloc",事实上,这是一个内存池,而后面所给的缺省值是STL库中提供的一个默认的内存池,它可以有效的提高开辟空间的时间消耗,但是比较复杂,所以在这里我们直接new空间,在之后,我们会专门的进行讲解

                (1).空构造:
                

                在这里实现的空构造是非常简单的,这是由于我们在声明时已经给了缺省值,但是该空构造是一定要写的,这是因为我们还要实现别的构造函数,这时候编译器就不在自动生成默认构造函数了

                (2).用n个元素对象进行初始化:
                

                在使用n个元素对象进行初始化时,我们在参数部分给了一个缺省值T(),很明显,这是一个匿名对象,同时调用了对应的默认构造函数,这是没有问题的,但是如果T是int、float等内置类型呢?事实上,这点不用担心,这是由于C++将这些内置类型都进行了升级,使它们可以象自定义类型一样调用默认构造函数,举个例子:如果T是int类型,那么此时T()就会返沪一个0进行初始化

                (3).使用一个迭代器区间进行初始化:
                

                在上面的构造函数中,我们再次使用了模板,但这里是函数模板,这是由于我们不仅要支持顺序表的迭代器类型初始化,还要支持其它类型的迭代器初始化

        3、返回顺序表一些性质的函数

                (1).size函数:返回此时vector中的元素个数

                库中的函数头:
                

                实现:
                

                (2).capacity函数:返回此时vector中的空间大小

                库中的函数头:
                

                实现:
                

        4、拷贝构造函数

        库中的函数头:

        

        实现:
        

        这里需要注意:我们不可以使用memcpy,这是由于memcpy会按字节将x的内容拷贝到_start,但如果T类型是动态开辟内存,也就是说是深拷贝的话,那么虽然vector整体进行了深拷贝,但是vector中的内容却只完成了浅拷贝,会出现深浅拷贝的问题,此时选择上面的方式就万无一失了,这要求元素对象重载了赋值运算符,这一点是必须的

        5、迭代器相关函数

                (1).begin函数:


                库中的函数头:
                

                begin函数进行了两个重载,分别是普通begin和const begin它们分别可以返回一个可读可写的迭代器和一个只读不写的迭代器,都指向vector的首元素位置:
                

                (2).end函数:


                库中的函数头:
                

                end函数仍然进行了两个重载,返回的迭代器都指向末尾元素的下一个位置:
                

        6、[]操作符重载

        库中的函数头:
        

        库中对[]操作符进行了两个重载,与上面的begin类似:
        

        库中对于很多类型进行了封装,事实上上面我们实现的与库中的本质是一样的

        7、交换函数

        库中的函数头:
        

        这里的交换一定是要涉及深拷贝的,在这里我们借用一下算法库中的swap函数,因为它恰好就是深拷贝:
        

        8、赋值运算符重载

        库中的函数头:
        

        在这里我们对该函数实现进行一点小改动,但是对于用户的使用来说是相同的:
        

        在上面的实现中,我们采用了传值传参,此时形参是实参的拷贝,拷贝会调用我们之前实现过的拷贝构造函数进行深拷贝,再调用swap函数,它也会进行深拷贝之下的交换,最终*this就变成了x的拷贝,最后x这一拷贝出作用域销毁,调用析构函数(后面会实现),进行了空间的释放

        9、开空间相关函数

                (1).reserve函数:会开辟指定大小的空间,如果原来有元素,会进行拷贝,否则不进行任何处理

                库中的函数头:
                

                实现:
                

                在上面我们又遇到了象拷贝构造函数同样的问题,所以再次使用了赋值运算符重载的方式进行处理

                (2).resize函数:会开辟指定大小的空间,用val进行初始化,缺省值为T()

                库中的函数头:
                

                实现:
                

                这里我们直接对之前的函数进行复用即可

        10、插入相关函数

                (1).insert函数:在迭代器指定的位置直接插入一个值,返回最后该位置的迭代器

                库中的函数头:
                

                实现:
                

                从上面的代码可以看出:在insert函数内部可能开辟空间,这时候pos就不是原来的pos了,所以在函数外部,传给过insert函数的迭代器是不能再次使用的,因为使用过的迭代器可能已经失效,这时候我们通过反回值的方式解决了这个问题,所以如果想再次使用的话,要接受函数的返回值

                (2).push_back函数:在vector尾部插入一个对象

                库中的函数头:
                

                实现:
                

                事实上在这个位置我们直接复用刚才写过的insert就非常方便

        11、删除相关函数

                (1).erase函数:删除迭代器指定位置的元素,返回该位置的迭代器

                库中的函数头:
                

                实现:
                

                erase所删除的位置有可能是最后一个,此时删除之后传入的迭代器·就失效了,所以要接收返回值并判断

                (2).pop_back函数

                库中的函数头:

                

                实现:
                

                直接复用刚才写过的erase函数即可

        12、析构函数

        由于析构函数的特殊性,这里就不提供库中的函数头了:

        

四、vector

        下面就是我们今天一起完成的vector了:
        

#include <iostream>
#include <cassert>
#include <algorithm>
using namespace std;
namespace bea
{template<class T>class vector{typedef T* iterator;typedef const T* const_iterator;public://空构造vector() {}//用n个元素对象进行初始化vector(size_t n, const T& val = T()){_start = new T[n + 5];for (size_t i = 0; i < n; i++){_start[i] = val;}_finish = _start + n;_endofstorge = _start + n + 4;}//使用一个迭代器区间进行初始化template<class InputIterator>vector(InputIterator first, InputIterator last){size_t n = last - first;_start = new T[n + 5];InputIterator it1 = first, it2 = _start;while (it1 < last){*it2 = *it1;it1++, it2++;}_finish = _start + n;_endofstorge = _start + n + 4;}//拷贝构造函数vector(const vector& x){size_t sz = x.size();_start = new T[sz + 5];for (size_t i = 0; i < sz; i++){_start[i] = x._start[i];}_finish = _start + sz;_endofstorge = _start + sz + 4;}//size函数size_t size() const{return _finish - _start;}//capacity函数size_t capacity() const{return _endofstorge - _start + 1;}//begin函数iterator begin(){return _start;}const_iterator begin() const{return _start;}//end函数iterator end(){return _finish;}const_iterator end() const{return _finish;}//[]操作符重载T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}//交换函数void swap(vector& x){std::swap(x._start, _start);std::swap(x._finish, _finish);std::swap(x._endofstorge, _endofstorge);}//赋值运算符重载vector& operator=(const vector x){swap(x);return *this;}//开空间相关函数void reserve(size_t n){size_t sz = size();if (n <= size()) return;iterator tmp = new T[n + 5];if (_start){for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;}_finish = tmp + sz;_start = tmp;_endofstorge = tmp + n + 4;}//insert函数iterator insert(iterator pos, const T& val){assert(pos <= _finish);int n = pos - _start;if (capacity() < size() + 1){reserve(size() + 5);}pos = _start + n;iterator end = _finish - 1, next = _finish;while (next > pos){*next = *end;end--, next--;}*pos = val;_finish++;return pos;}void push_back(const T& val){insert(_finish, val);}void resize(size_t n, T& val = T()){vector(n, val);}//erase函数iterator erase(iterator pos){assert(pos < _finish);iterator end = pos + 1, front = pos;while (end < _finish){*front = *end;front++, end++;}_finish--;if (pos == _finish) return nullptr;else return pos;}//pop_back函数void pop_back(){erase(_finish - 1);}//析构函数~vector(){delete[] _start;_start = nullptr;_finish = nullptr;_endofstorge = nullptr;}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorge = nullptr;};
}

五、结语

        这就是我们所实现的vector的全部内容了,我们的目的仍然是了解vector的用法、加深类和对象和模板等知识点的理解,感谢大家的阅读,欢迎各位于晏、亦菲和我一起交流、学习、进步!

                

               

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

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

相关文章

C++学习之路,从0到精通的征途:继承

目录 一.继承的概念及定义 1.继承的概念 2.继承的定义 (1)继承的定义格式 (2)继承基类成员访问方式的变化 二.基类与派生类间的转换 1.派生类对象赋值给基类的引用/指针 2. 派生类对象直接赋值给基类对象 三.继承的作用域 四.派生类的默认成员函数 1.构造函数 2.拷…

用vue和go实现登录加密

前端使用CryptoJS默认加密方法&#xff1a; var pass CryptoJS.AES.encrypt(formData.password, key.value).toString()使用 CryptoJS.AES.encrypt() 时不指定加密模式和参数时&#xff0c;CryptoJS 默认会执行以下操作 var encrypted CryptoJS.AES.encrypt("明文&quo…

React百日学习计划——Deepseek版

阶段一&#xff1a;基础巩固&#xff08;1-20天&#xff09; 目标&#xff1a;掌握HTML/CSS/JavaScript核心语法和开发环境搭建。 每日学习内容&#xff1a; HTML/CSS&#xff08;1-10天&#xff09; 标签语义化、盒模型、Flex布局、Grid布局、响应式设计&#xff08;媒体查询…

WPF中如何自定义控件

WPF自定义控件简化版&#xff1a;账户菜单按钮&#xff08;AccountButton&#xff09; 我们以**“账户菜单按钮”为例&#xff0c;用更清晰的架构实现一个支持标题显示、渐变背景、选中状态高亮**的自定义控件。以下是分步拆解&#xff1a; 一、控件核心功能 我们要做一个类似…

Deepseek+Xmind:秒速生成思维导图与流程图

deepseekxmind&#xff0c;快速生成思维导图和流程图 文章目录 思维导图deepseek笔记本 txt文件xmind 流程图deepseekdraw.io 思维导图 deepseek 笔记本 txt文件 将deep seek的东西复制到文本文件中&#xff0c;然后将txt文件拓展名改成md xmind 新建思维导图----左上角三…

基于javaweb的SpringBoot爱游旅行平台设计和实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文…

服务器机架的功能和重要性

服务器已经成为各个行业必不可少的网络设备&#xff0c;而服务器机架则是数据中心和IT基础设施中不可或缺的重要组成部分&#xff0c;服务器机架能够为服务器和其他网络设备提供物理支撑&#xff0c;同时还可以提供设备维护和管理等多种功能&#xff0c;本文就来介绍一下服务器…

游戏引擎学习第277天:稀疏实体系统

回顾并为今天定下基调 上次我们结束的时候&#xff0c;基本上已经控制住了跳跃的部分&#xff0c;达到了我想要的效果&#xff0c;现在我们主要是在等待一些新的艺术资源。因此&#xff0c;等新艺术资源到位后&#xff0c;我们可能会重新处理跳跃的部分&#xff0c;因为现在的…

阿克曼-幻宇机器人系列教程1- 实现上位机与下位机交互的两种方式

1. 电脑与机器人通过SSH命令连接 1.1 将机器人上电 目的&#xff1a;将机器人变成热点 目标&#xff1a;将电脑连接机器人网络 热点名称&#xff1a;Huanyu-111 密码&#xff1a;12345678 1.2 完成电脑与机器人之间的连接 实现&#xff1a;在电脑终端中执行命令通过SSH登录…

Rust 中的 Pin 和 Unpin:内存安全与异步编程的守护者

在 Rust 的世界里&#xff0c;Pin 和 Unpin 是两个看似不起眼、实则至关重要的概念。它们在内存安全和异步编程中扮演着关键角色&#xff0c;是 Rust 开发者必须掌握的知识。今天&#xff0c;就让我们深入探讨这两个概念&#xff0c;看看它们是如何在 Rust 的生态系统中发挥作用…

如何界定合法收集数据?

首席数据官高鹏律师团队 在当今数字化时代&#xff0c;数据的价值日益凸显&#xff0c;而合法收集数据成为了企业、机构以及各类组织必须严守的关键准则。作为律师&#xff0c;深入理解并准确界定合法收集数据的范畴&#xff0c;对于保障各方权益、维护法律秩序至关重要。 一…

自动驾驶的“眼睛”:用Python构建智能障碍物检测系统

自动驾驶的“眼睛”:用Python构建智能障碍物检测系统 在自动驾驶技术日益成熟的今天,障碍物检测系统成了汽车智能化不可或缺的部分。无论是高速公路上的突发状况,还是城市街道中的行人与车辆,准确识别障碍物并及时反应,是保证行车安全的关键。 那么,我们如何用Python构…

19.Excel数据透视表:第2部分数据透视计算

一 日期组合 不想看具体是哪一天的收入&#xff0c;想看每个月的收入是多少&#xff0c;要对日期进行组合。 光标选中日期字段下的数据&#xff0c; 右键。 补充&#xff1a;第2种方法。 补充&#xff1a;可以同时选择多个。 下面这个是错误的。 源数据里面有不同的年份&#x…

Eclipse 插件开发 6 右键菜单

Eclipse 插件开发 6 右键菜单 1 plugin.xml2 SampleHandler.java3 Activator.java 1 plugin.xml <?xml version"1.0" encoding"UTF-8"?> <?eclipse version"3.4"?> <plugin><!-- 定义命令 --><extension point&…

用vite脚手架建立 前端工程

​ 参考 开始 | Vite 官方中文文档 脚本 chcp 65001 echo 建立vite工程 set PRO_NAMEmy-vue-app call npm create vitelatest %PRO_NAME% --template vue cd ./%PRO_NAME%set NOW_PATH%cd% echo now_path %NOW_PATH% echo 点击回车启动vite工程&#xff0c;请访问ht…

ESP32C3连接wifi

文章目录 &#x1f527; 一、ESP32-C3 连接 Wi-Fi 的基本原理&#xff08;STA 模式&#xff09;✅ 二、完整代码 注释讲解&#xff08;适配 ESP32-C3&#xff09;&#x1f4cc; 三、几个关键点解释&#x1f51a; 四、小结 &#x1f527; 一、ESP32-C3 连接 Wi-Fi 的基本原理&a…

LangSmith 基本使用教程

LangSmith 是一个强大的工具&#xff0c;可以帮助开发者追踪、监控和分析语言模型应用程序的性能。下面我将介绍两种基本的追踪方式&#xff1a;追踪 OpenAI 调用和追踪整个应用程序。 1. 追踪 OpenAI 调用 (Trace OpenAI calls) 这种方法主要用于追踪对 OpenAI API 的调用&a…

Python基础学习-Day23

目录 基础概念转换器&#xff08;transformer&#xff09;估计器&#xff08;estimator&#xff09;管道&#xff08;pipeline&#xff09; 实例pipeline 基础概念 pipeline在机器学习领域可以翻译为“管道”&#xff0c;也可以翻译为“流水线”&#xff0c;是机器学习中一个重…

相对论速度叠加公式与双曲正切

复习下相对论速度叠加公式吧&#xff0c;物理&#xff0c;是不是很多人都忘了呀。假设速度为 u , v u,v u,v&#xff0c;那么叠加后的速度 w w w为&#xff1a; w u v 1 u v / c 2 w\frac{uv}{1uv/c^2} w1uv/c2uv​   这个公式告诉我们&#xff0c;在一个速度为2/3光速的…

【前缀和】和为 K 的子数组(medium)

【前缀和】和为 K 的子数组 题目描述算法原理和细节问题代码 题目描述 和为 K 的子数组 给定一个整数数组和一个整数 k &#xff0c;请找到该数组中和为 k 的连续子数组的个数。 示例 1&#xff1a; 输入:nums [1,1,1], k 2 输出: 2 解释: 此题 [1,1] 与 [1,1] 为两种不同的…