【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…

第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 查…

springmvc下

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

目标跟踪—卡尔曼滤波

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

【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;提供了一个创新且高效的方式帮助开发者推广他们的应用…

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…

STM32 串口IDLE接收空闲中断+DMA

参考 http://t.csdnimg.cn/fAV38 1.基础知识 STM32 IDLE 接收空闲中断 功能&#xff1a; 在使用串口接受字符串时&#xff0c;可以使用空闲中断&#xff08;IDLEIE置1&#xff0c;即可使能空闲中断&#xff09;&#xff0c;这样在接收完一个字符串&#xff0c;进入空闲状态时&…

(三)Appdesigner-界面转换及数据导入和保存

提示&#xff1a;文章为系列文章&#xff0c;可以在对应学习专栏里面进行学习。对应资源已上传 目录 前言 一、Appdesigner是什么&#xff1f; 二、界面切换 三、数据导入及保存 &#xff08;一&#xff09;数据导入 &#xff08;二&#xff09;数据保存 总结 前言 Appd…

ubuntu搭建kms服务器

1.下载kms开源包(如果提示找不到wget命令的话:apt install wget): wget https://github.com/Wind4/vlmcsd/releases/download/svn1111/binaries.tar.gz2.解压: tar -xzvf binaries.tar.gz接着cd 进入 Linux/intel/static/ 文件夹下: 3.选择对应的文件&#xff0c;这里我们选…

C++:继承-继承权限

在C中&#xff0c;类的权限分为公有、私有和保护三种。这些权限控制了类的成员&#xff08;数据成员和成员函数&#xff09;对外部代码的可见性和访问性。 公有&#xff08;public&#xff09;权限&#xff1a; 在公有权限下声明的成员可以被类的外部代码直接访问&#xff1b;公…

第十篇:深入文件夹:Python中的文件管理和自动化技术

深入文件夹&#xff1a;Python中的文件管理和自动化技术 1 文件系统基础操作 在今天的技术博客中&#xff0c;我们将深入探讨Python中的文件系统基础操作。文件系统对于任何操作系统都是不可或缺的组成部分&#xff0c;它管理着数据的存储、检索以及维护。Python通过其标准库中…

【Linux 进程】 自定义shell

目录 关于shell 1.打印提示符&&获取用户命令字符​编辑 2.分割字符串 3.检查是否为内建命令 cd命令 export命令 echo命令 1.输出最后一个执行的命令的状态退出码&#xff08;返回码&#xff09; 2.输出指定环境变量 4.执行外部命令 关于shell Shell 是计算机操…