【C++】学习笔记——vector_2

文章目录

  • 七、vector
    • 2. vecotr的使用
    • 3. vector的模拟实现
  • 未完待续


七、vector

2. vecotr的使用

上节我们以二维数组结束,这一节我们以二维数组开始。

// 二维数组
vector<vector<int>> vv;

二维数组在底层是连续的一维数组vv[i][j] 是怎样访问的?是 * ( *(a + i) + j)

3. vector的模拟实现

细细的学习了 string 之后, vector 上手就非常简单,所以我们现在就来实现一下 vector 。注意,部分内容和 STL 里的 vector 保持一致,需要理解。
我们先开一个头文件:vector.h。把框架补上:

#pragma oncenamespace my
{template<class T>class vector{public:typedef T* iterator;private:// 数据起始地址iterator _start = nullptr;// 数据末尾地址iterator _finish = nullptr;// 容量末尾地址iterator _endofstorage = nullptr;};
}

简单实现一下插入数据:

// vector.h
#pragma once#include<assert.h>namespace my
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;vector(){}~vector(){delete[] _start;_start = _finish = _endofstorage = nullptr;}size_t size() const{// 数据个数return _finish - _start;}size_t capacity() const{// 容量大小return _endofstorage - _start;}// 下标 + [] 访问T& operator[](size_t pos){assert(pos < size());return _start[pos];}void push_back(const T& val){// 判断扩容if (_finish == _endofstorage){// 保留 sizesize_t old_size = size();// 2倍扩容size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();// 开空间T* tmp = new T[newcapacity];// 拷贝内容, 注意字节数memcpy(tmp, _start, size() * sizeof(T));// 释放旧空间delete[] _start;// 更改地址_start = tmp;_finish = _start + old_size;_endofstorage = _start + newcapacity;}// 插入数据*_finish = val;++_finish;}private:// 数据起始地址iterator _start = nullptr;// 数据末尾的下一个地址iterator _finish = nullptr;// 容量末尾的下一个地址iterator _endofstorage = nullptr;};void test01(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);for (int i = 0; i < v.size(); ++i){std::cout << v[i] << " ";}std::cout << std::endl;}
}

注意细节:在 push_back 函数里面,扩容的时候需要保存旧的 size 的值,当发生扩容时,_finish 已经发生改变,会出严重错误!!。
在这里插入图片描述

我们将测试函数放在 my命名空间内,vector 类外
然后是源文件:test.cpp

// test.cpp
#include<iostream>
#include"vector.h"
using namespace std;int main()
{// 命名空间域内的测试函数my::test01();return 0;
}

在这里插入图片描述
嗯,不错,接下来实现迭代器,还是那句话,迭代器是行为像指针的东西,不一定是指针,但是我们简单的模拟实现就将其当作指针实现:

// 类内
iterator begin()
{return _start;
}iterator end()
{return _finish;
}const_iterator begin() const
{return _start;
}const_iterator end() const
{return _finish;
}
// 测试区
void test02()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);vector<int>::iterator it = v.begin();while (it != v.end()){std::cout << *it << " ";++it;}std::cout << std::endl;for (auto e : v){std::cout << e << " ";}std::cout << std::endl;
}
// test.cpp
#include<iostream>
#include"vector.h"
using namespace std;int main()
{my::test02();return 0;
}

在这里插入图片描述
我们需要一个通用的扩容函数 reserve

void reserve(size_t n)
{// 比当前容量大才允许扩容if (n > capacity()){size_t old_size = size();// 开空间T* tmp = new T[n];// 拷贝内容, 注意字节数memcpy(tmp, _start, size() * sizeof(T));// 释放旧空间delete[] _start;// 更改地址_start = tmp;_finish = _start + old_size;_endofstorage = _start + n;}
}

于是,我们的 push_back 函数可以复用 reserve 了。

void push_back(const T& val)
{// 判断扩容if (_finish == _endofstorage){reserve(capacity() == 0 ? 4 : 2 * capacity());}// 插入数据*_finish = val;++_finish;
}

接下来实现尾删 pop_back 和判空函数 empty

// 尾删
void pop_back()
{assert(!empty());--_finish;
}// 判断容器是否为空
bool empty()
{return _start == _finish;
}

还有 insert 插入函数:

void insert(iterator pos, const T& val)
{assert(pos >= _start);assert(pos <= _finish);// 记录相对位置size_t len = pos - _start;if (_finish == _endofstorage){reserve(capacity() == 0 ? 4 : 2 * capacity());}// 如果发生异地扩容,需要更新 迭代器 ,否则将会发生迭代器失效pos = _start + len;iterator it = _finish - 1;while (it >= pos){*(it + 1) = *it;--it;}*pos = val;++_finish;
}

测试一下:

// 测试区
void test03()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);// 头部插入一个0v.insert(v.begin(), 0);// 尾删v.pop_back();for (auto e : v){std::cout << e << " ";}std::cout << std::endl;
}
// test.cpp
#include<iostream>
#include"vector.h"
using namespace std;int main()
{my::test03();return 0;
}

在这里插入图片描述
既然 insert 没有问题,那么。。push_back 又可以复用了。

void push_back(const T& val)
{ 判断扩容//if (_finish == _endofstorage)//{//	reserve(capacity() == 0 ? 4 : 2 * capacity());//} 插入数据//*_finish = val;//++_finish;insert(end(), val);
}

非常nice啊,继续实现 erase

void erase(iterator pos)
{assert(pos >= _start);// 不要等于 _finishassert(pos < _finish);iterator it = pos + 1;while (it < _finish){*(it - 1) = *it;++it;}--_finish;
}

有了 erasepop_back 就可以复用了。

// 尾删
void pop_back()
{//assert(!empty());////--_finish;erase(end() - 1);--_finish;
}

再实现 resize 。我们先给个框架。很多人对后面的第二个参数有疑问,那是什么?

void resize(size_t n, const T& val = T())
{}

我们的 vector 使用的是模板,不能锁死任意一个类型,所以不能冒然的 等于0 啊之类的,万一是个类不久出大问题了吗?所以这里是 调用无参的匿名对象的构造函数 ,看是看懂了,但是,这不本末倒置了吗?,这让内置类型怎么办?内置类型难道有构造函数?嘿,你还真别说,内置类型还真有构造函数等等 。祖师爷就是为了解决这种问题,他将内置类型给升级成了,有点厉害啊。所以,内置类型也能这样玩:

int i = 1;
int i = int();
int i = int(2);

内置类型的默认值:int就是0,double就是0.0,指针类型就是nullptr 这样。这么看来,上面的第二个参数就能理解了吧。resize 的实现其实非常简单:

void resize(size_t n, const T& val = T())
{// 插入数据if (n > size()){reserve(n);while (_finish < _start + n){*_finish = val;++_finish;}}// 删除数据else{_finish = _start + n;}
}

ok,我们来测试一下:

// 测试区
void test04()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.resize(10);for (auto e : v){std::cout << e << " ";}std::cout << std::endl;v.resize(30, 7);for (auto e : v){std::cout << e << " ";}std::cout << std::endl;v.resize(3);for (auto e : v){std::cout << e << " ";}std::cout << std::endl;
}
// test.cpp
#include<iostream>
#include"vector.h"
using namespace std;int main()
{my::test04();return 0;
}

在这里插入图片描述
最后,再写一个 拷贝构造函数 ,这个拷贝构造函数将会非常 玄幻。直接上菜:

vector(const vector<T>& v)
{reserve(v.capacity());for (auto& e : v){push_back(e);}
}

没错,就是用 auto,自动匹配类型,创建出一个 vector< T >。
测试一下:

// 测试区
void test05()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);for (auto e : v){std::cout << e << " ";}std::cout << std::endl;vector<int> v1(v);for (auto e : v1){std::cout << e << " ";}std::cout << std::endl;
}
// tset.cpp
#include<iostream>
#include"vector.h"
using namespace std;int main()
{my::test05();return 0;
}

在这里插入图片描述
非常 nice 啊。


未完待续

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

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

相关文章

分布式与一致性协议之一致哈希算法(二)

一致哈希算法 使用哈希算法有什么问题 通过哈希算法&#xff0c;每个key都可以寻址到对应的服务器&#xff0c;比如&#xff0c;查询key是key-01,计算公式为hash(key-01)%3,警告过计算寻址到了编号为1的服务器节点A&#xff0c;如图所示。 但如果服务器数量发生变化&#x…

vue3使用el-autocomplete请求远程数据

服务器端 RestController RequestMapping("/teacher") public class TeacherController {Resourceprivate TeacherService teacherService;GetMapping({"/v1/getTop10TeacherByName/","/v1/getTop10TeacherByName/{name}"})public ResultBean&l…

快速批量重命名文件(夹)

首先&#xff0c;需要用到的这个工具&#xff1a; 度娘网盘 提取码&#xff1a;qwu2 蓝奏云 提取码&#xff1a;2r1z 我这里处理这4个文本&#xff0c;实际可以处理任意数量的文本和文件夹 1、打开工具&#xff0c;进入文件批量复制版块 2、点击“重命名” 3、把要重命名的…

使用Python爬取淘宝商品并做数据分析

使用Python爬取淘宝商品并做数据分析&#xff0c;可以按照以下步骤进行操作&#xff1a; 确定需求&#xff1a;确定要爬取的淘宝商品的种类、数量、关键词等信息。 编写爬虫程序&#xff1a;使用Python编写爬虫程序&#xff0c;通过模拟浏览器请求&#xff0c;获取淘宝商品的页…

Docker 中的 Nginx 服务为什么要启用 HTTPS

一安装容器 1 安装docker-20.10.17 2 安装所需的依赖 sudo yum install -y yum-utils device-mapper-persistent-data lvm23 添加Docker官方仓库 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo4 安装Docker CE 20.10.17 s…

python制作可执行文件(cython)

使用Cython将Python脚本编译成可执行文件涉及几个步骤。以下是一个基本的指南&#xff1a; 1. 安装Cython 首先&#xff0c;你需要安装Cython。你可以使用pip来安装&#xff1a; pip install cython 2. 编写Cython文件 通常&#xff0c;Cython源文件的后缀是.pyx。你可以将…

第182期 23ai:惊喜的全功能缓存True Cache-2安装部署(20240505)

数据库管理182期 2024-05-05 数据库管理-第182期 23ai:惊喜的全功能缓存True Cache-2安装部署&#xff08;20240505&#xff09;1 主机配置2 操作系统配置2.1 基础配置2.2 配置hosts2.3 安装preinstall RPM包2.4 创建目录2.5 配置环境变量 3 部署数据库3.1 部署DB软件3.2 创建监…

机器学习:基于K-近邻(KNN)、高斯贝叶斯(GaussianNB)、SVC、随机森林(RF)、梯度提升树(GBDT)对葡萄酒质量进行预测

前言 系列专栏&#xff1a;机器学习&#xff1a;高级应用与实践【项目实战100】【2024】✨︎ 在本专栏中不仅包含一些适合初学者的最新机器学习项目&#xff0c;每个项目都处理一组不同的问题&#xff0c;包括监督和无监督学习、分类、回归和聚类&#xff0c;而且涉及创建深度学…

linux上如何排查JVM内存过高?

怎么排查JVM内存过高&#xff1f; 前言&#xff1a; 想必工作一两年以后的同学都会逐渐面临到&#xff0c;jvm等问题&#xff0c;但是可能苦于无法熟练的使用一些工具&#xff1b;本文将介绍几个比较常用分析工具的使用方法&#xff0c;带着大家一步步定位分析问题。 1、top 查…

uni-app 从vue3项目创建到Pinia管理数据全局使用 持久化存储数据 详细教程

一、创建uni-app项目 1. 安装HBuilder X&#xff0c;下载地址&#xff1a;https://www.dcloud.io/hbuilderx.html 2. 打开HBuilder X&#xff0c;点击左上角的“文件”->“新建”->“项目”&#xff0c;选择“uni-app”项目模板&#xff0c;填写项目名称和项目路径&…

从 Word 文档中提取所有的有效 JSON 对象(包含跨段落)

文章目录 一、概述二、代码 一、概述 从 word 中提取所有有效 json &#xff08;包含跨段落的 json&#xff09;。 二、代码 """ 从 Word 文档中提取所有的 JSON 对象 """from docx import Document import jsondef extract_json_from_docx(d…

springmvc下

第二类初始化操作 multipartResolver应用 localeResolver应用 themeResolver应用 handlerMapping应用 handlerAdapter应用 handlerExceptionReslver requestToViewNameTranslator应用 viewResolver应用 flashMapManager应用 dispatcherServlet逻辑处理 processRequest处理web请…

目标跟踪—卡尔曼滤波

目标跟踪—卡尔曼滤波 卡尔曼滤波引入 滤波是将信号中特定波段频率滤除的操作&#xff0c;是抑制和防止干扰的一项重要措施。是根据观察某一随机过程的结果&#xff0c;对另一与之有关的随机过程进行估计的概率理论与方法。 历史上最早考虑的是维纳滤波&#xff0c;后来R.E.卡…

相差8小时:时区设置导致docker中的openGauss时间问题

文章目录 &#xff08;一&#xff09;问题&#xff08;二&#xff09;解决&#xff08;2.1&#xff09;确认服务器时间&#xff08;2.2&#xff09;确认和修改docker时间&#xff08;2.2.1&#xff09;失败的尝试&#xff08;2.2.2&#xff09;成功的尝试 &#xff08;2.2&…

OpenCV特征检测

1. 特征检测的基本概念 特征检测是计算机视觉和图像处理中的一个概念。它指的是使用计算机提取图像信息&#xff0c;决定每个图像的点是否属于一个图像特征。特征检测的结果是把图像上的点 分为不同的子集&#xff0c;这些子集往往属于孤立的点、连续的曲线或者连续的区域。 …

【C语言】分支和循环(上)

【C语言】分支和循环&#xff08;上&#xff09; 1、if语句1.2 else1.3分支中包含多条语句1.4嵌套if1.5悬空else问题 2、关系操作符3、条件操作符4、逻辑操作符&#xff1a;与、或、非&#xff08;取反&#xff09;&#xff08;&&&#xff0c;||&#xff0c;&#xff0…

小猪APP分发平台 – 掌握移动应用的推广新途径

在移动互联网高速发展的背景下小猪APP分发平台 – 掌握移动应用的推广新途径&#xff0c;app分发已成为开发者和企业关注的重点。小猪APP分发平台作为行业内的新星小猪APP分发平台 – 掌握移动应用的推广新途径&#xff0c;提供了一个创新且高效的方式帮助开发者推广他们的应用…

attr 与prop 的区别

在前端开发中&#xff0c;attr 和 prop 是两个常用的方法&#xff0c;用于操作 HTML 元素的属性和属性值。 它们之间的区别主要在于针对不同类型的属性操作。 attr&#xff08;&#xff09;&#xff1a; attr() 是 jQuery 中用来获取或设置 HTML 元素的属性的方法。它可以用于获…

ZOC8 for Mac v8.08.1激活版:卓越性能的SSH客户端

在远程连接和管理的世界中&#xff0c;ZOC8 for Mac以其卓越的性能和丰富的功能&#xff0c;成为了众多专业人士的首选SSH客户端。它支持SSH1、SSH2、Telnet、Rlogin、Serial等多种协议&#xff0c;让您轻松连接到远程服务器。ZOC8拥有简洁直观的界面和强大的功能设置&#xff…

SQL 基础 | JOIN 操作介绍

在SQL中&#xff0c;JOIN是一种强大的功能&#xff0c;用于将两个或多个表中的行结合起来&#xff0c;基于相关的列之间的关系。 JOIN操作通常用在SELECT语句中&#xff0c;以便从多个表中检索数据。 以下是几种基本的JOIN类型以及它们的用法&#xff1a; INNER JOIN&#xff1…