【C++庖丁解牛】vector容器的简易模拟实现(C++实现)(最后附源码)

🍁你好,我是 RO-BERRY
📗 致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识
🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油

在这里插入图片描述

目录

  • 前言
  • vector容器代码实现
    • 内部成员简介
    • 构造函数
    • 拷贝函数
    • 析构函数
    • 迭代器相关
    • 容量相关
    • 元素访问
    • vector的修改操作
  • 源代码


前言

我们前面介绍了vector容器的概念以及对其基本使用进行了介绍,如果你在这里不知道vector是什么以及不知道如何使用的话,可以进入本人主页,在C++专栏里有介绍

为了对小白友好,在这我简单介绍一下

C++中的vector是一个动态数组容器,可以存储不同类型的元素。它提供了一系列的成员函数来方便地操作和管理数组。

以下是C++ vector容器的一些特点和功能:

  1. 动态大小:vector的大小可以根据需要动态调整,可以在运行时添加或删除元素。
  2. 随机访问:可以通过索引直接访问vector中的元素,支持常数时间的随机访问。
  3. 自动内存管理:vector会自动处理内存分配和释放,无需手动管理内存。
  4. 插入和删除:可以在任意位置插入或删除元素,vector会自动调整其他元素的位置。
  5. 迭代器支持:可以使用迭代器遍历vector中的元素。
  6. 动态增长:当vector的容量不足时,会自动重新分配更大的内存空间,以容纳更多的元素。
  7. 元素访问:可以使用下标运算符[]或at()函数来访问元素,也可以使用front()和back()函数分别获取第一个和最后一个元素。

本章节主要对vector容器的手撕实现其简单功能

vector容器代码实现

内部成员简介

命名空间实现与库里vector的隔绝,实现自定义vector

namespace A
{template<class T>   //模版实现vector<类型>class vector        //vector功能实现{public:// Vector的迭代器是一个原生指针typedef T* iterator;typedef const T* const_iterator;.......           //函数接口实现.......private:iterator _start;		// 指向数据块的开始iterator _finish;		// 指向有效数据的尾iterator _endOfStorage;  // 指向存储容量的尾};
}

构造函数

		vector(): _start(nullptr), _finish(nullptr), _endOfStorage(nullptr){}vector(size_t n, const T& value = T()): _start(nullptr), _finish(nullptr), _endOfStorage(nullptr){reserve(n);while (n--){push_back(value);}}

理论上讲,提供了vector(size_t n, const T& value = T())之后
vector(int n, const T& value = T())就不需要提供了,但是对于:
vector v(10, 5);
编译器在编译时,认为T已经被实例化为int,而10和5编译器会默认其为int类型
就不会走vector(size_t n, const T& value = T())这个构造方法,
最终选择的是:vector(InputIterator first, InputIterator last)
因为编译器觉得区间构造两个参数类型一致,因此编译器就会将InputIterator实例化为int
但是10和5根本不是一个区间,编译时就报错了
故需要增加如下构造方法

vector(int n, const T& value = T()): _start(new T[n]), _finish(_start + n), _endOfStorage(_finish)
{for (int i = 0; i < n; ++i){_start[i] = value;}
}

拷贝函数

若使用iterator做迭代器,会导致初始化的迭代器区间[first,last)只能是vector的迭代器
重新声明迭代器,迭代器区间[first,last)可以是任意容器的迭代器

		template<class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}vector(const vector<T>& v): _start(nullptr), _finish(nullptr), _endOfStorage(nullptr){reserve(v.capacity());iterator it = begin();const_iterator vit = v.cbegin();while (vit != v.cend()){*it++ = *vit++;}_finish = it;}vector<T>& operator=(vector<T> v){swap(v);return *this;}

析构函数

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

迭代器相关

迭代器是vector中用于遍历元素的一种工具。通过迭代器,我们可以访问vector中的每个元素,并对其进行操作。

vector的迭代器有以下几种类型:

begin():返回指向vector第一个元素的迭代器。
end():返回指向vector最后一个元素之后位置的迭代器。
rbegin():返回指向vector最后一个元素的逆向迭代器。
rend():返回指向vector第一个元素之前位置的逆向迭代器。

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

容量相关

下面是vector的容量相关接口:

size():返回vector中元素的个数。
capacity():返回当前vector可以容纳的元素个数,即当前分配的内存空间大小。
max_size():返回vector能够容纳的最大元素个数,这个值取决于系统的限制。
resize():改变vector的大小,可以增加或减少元素的个数。
reserve():预留内存空间,用于存放指定数量的元素,避免频繁的内存重新分配。
empty():判断vector是否为空,如果为空则返回true,否则返回false。

		size_t size() const{return _finish - _start;}size_t capacity() const{return _endOfStorage - _start;}bool empty() const{return _start == _finish;}void reserve(size_t n){if (n > capacity()){size_t oldSize = size();// 1. 开辟新空间T* tmp = new T[n];// 2. 拷贝元素// 这里直接使用memcpy会有问题吗?同学们思考下//if (_start)//	memcpy(tmp, _start, sizeof(T)*size);if (_start){for (size_t i = 0; i < oldSize; ++i)tmp[i] = _start[i];// 3. 释放旧空间delete[] _start;}_start = tmp;_finish = _start + oldSize;_endOfStorage = _start + n;}}void resize(size_t n, const T& value = T()){// 1.如果n小于当前的size,则数据个数缩小到nif (n <= size()){_finish = _start + n;return;}// 2.空间不够则增容if (n > capacity())reserve(n);// 3.将size扩大到niterator it = _finish;_finish = _start + n;while (it != _finish){*it = value;++it;}}

元素访问

下面是vector的元素访问相关接口:

operator[]:通过索引访问vector中的元素。例如,vector_name[index]可以获取vector中索引为index的元素。
at():通过索引访问vector中的元素,与operator[]类似,但会进行边界检查。如果索引超出范围,会抛出std::out_of_range异常。
front():获取vector中第一个元素。
back():获取vector中最后一个元素。
data():返回指向vector内部存储数据的指针。可以使用该指针直接访问vector中的元素。
size():返回vector中元素的个数。
empty():检查vector是否为空,如果为空则返回true,否则返回false。
clear():清空vector中的所有元素。
push_back():在vector的末尾添加一个元素。
pop_back():删除vector末尾的元素。

		T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos)const{assert(pos < size());return _start[pos];}T& front(){return *_start;}const T& front()const{return *_start;}T& back(){return *(_finish - 1);}const T& back()const{return *(_finish - 1);}

vector的修改操作

下面是vector的一些常用的修改操作相关接口:

push_back:在vector的末尾插入一个元素。
pop_back:删除vector的最后一个元素。
insert:在指定位置插入一个或多个元素。
erase:删除指定位置的一个或多个元素。
clear:清空vector中的所有元素。
resize:改变vector的大小。
swap:交换两个vector的内容。
assign:用新元素替换vector中的内容。

		void push_back(const T& x){insert(end(), x);}void pop_back(){erase(end() - 1);}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endOfStorage, v._endOfStorage);}iterator insert(iterator pos, const T& x){assert(pos <= _finish);// 空间不够先进行增容if (_finish == _endOfStorage){//size_t size = size();size_t newCapacity = (0 == capacity()) ? 1 : capacity() * 2;reserve(newCapacity);// 如果发生了增容,需要重置pospos = _start + size();}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;}// 返回删除数据的下一个数据// 方便解决:一边遍历一边删除的迭代器失效问题iterator erase(iterator pos){// 挪动数据进行删除iterator begin = pos + 1;while (begin != _finish) {*(begin - 1) = *begin;++begin;}--_finish;return pos;}

源代码

#pragma once#include <iostream>
using namespace std;
#include <assert.h>namespace A
{template<class T>class vector{public:// Vector的迭代器是一个原生指针typedef T* iterator;typedef const T* const_iterator;///// 构造和销毁vector(): _start(nullptr), _finish(nullptr), _endOfStorage(nullptr){}vector(size_t n, const T& value = T()): _start(nullptr), _finish(nullptr), _endOfStorage(nullptr){reserve(n);while (n--){push_back(value);}}/** 理论上将,提供了vector(size_t n, const T& value = T())之后* vector(int n, const T& value = T())就不需要提供了,但是对于:* vector<int> v(10, 5);* 编译器在编译时,认为T已经被实例化为int,而10和5编译器会默认其为int类型* 就不会走vector(size_t n, const T& value = T())这个构造方法,* 最终选择的是:vector(InputIterator first, InputIterator last)* 因为编译器觉得区间构造两个参数类型一致,因此编译器就会将InputIterator实例化为int* 但是10和5根本不是一个区间,编译时就报错了* 故需要增加该构造方法*/vector(int n, const T& value = T()): _start(new T[n]), _finish(_start + n), _endOfStorage(_finish){for (int i = 0; i < n; ++i){_start[i] = value;}}// 若使用iterator做迭代器,会导致初始化的迭代器区间[first,last)只能是vector的迭代器// 重新声明迭代器,迭代器区间[first,last)可以是任意容器的迭代器template<class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}vector(const vector<T>& v): _start(nullptr), _finish(nullptr), _endOfStorage(nullptr){reserve(v.capacity());iterator it = begin();const_iterator vit = v.cbegin();while (vit != v.cend()){*it++ = *vit++;}_finish = it;}vector<T>& operator=(vector<T> v){swap(v);return *this;}~vector(){if (_start){delete[] _start;_start = _finish = _endOfStorage = nullptr;}}/// 迭代器相关iterator begin(){return _start;}iterator end(){return _finish;}const_iterator cbegin() const{return _start;}const_iterator cend() const{return _finish;}//// 容量相关size_t size() const{return _finish - _start;}size_t capacity() const{return _endOfStorage - _start;}bool empty() const{return _start == _finish;}void reserve(size_t n){if (n > capacity()){size_t oldSize = size();// 1. 开辟新空间T* tmp = new T[n];// 2. 拷贝元素// 这里直接使用memcpy会有问题吗?同学们思考下//if (_start)//	memcpy(tmp, _start, sizeof(T)*size);if (_start){for (size_t i = 0; i < oldSize; ++i)tmp[i] = _start[i];// 3. 释放旧空间delete[] _start;}_start = tmp;_finish = _start + oldSize;_endOfStorage = _start + n;}}void resize(size_t n, const T& value = T()){// 1.如果n小于当前的size,则数据个数缩小到nif (n <= size()){_finish = _start + n;return;}// 2.空间不够则增容if (n > capacity())reserve(n);// 3.将size扩大到niterator it = _finish;_finish = _start + n;while (it != _finish){*it = value;++it;}}///// 元素访问T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos)const{assert(pos < size());return _start[pos];}T& front(){return *_start;}const T& front()const{return *_start;}T& back(){return *(_finish - 1);}const T& back()const{return *(_finish - 1);}/// vector的修改操作void push_back(const T& x){insert(end(), x);}void pop_back(){erase(end() - 1);}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endOfStorage, v._endOfStorage);}iterator insert(iterator pos, const T& x){assert(pos <= _finish);// 空间不够先进行增容if (_finish == _endOfStorage){//size_t size = size();size_t newCapacity = (0 == capacity()) ? 1 : capacity() * 2;reserve(newCapacity);// 如果发生了增容,需要重置pospos = _start + size();}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;}// 返回删除数据的下一个数据// 方便解决:一边遍历一边删除的迭代器失效问题iterator erase(iterator pos){// 挪动数据进行删除iterator begin = pos + 1;while (begin != _finish) {*(begin - 1) = *begin;++begin;}--_finish;return pos;}private:iterator _start;		// 指向数据块的开始iterator _finish;		// 指向有效数据的尾iterator _endOfStorage;  // 指向存储容量的尾};
}/// /
/// 对模拟实现的vector进行严格测试
void TestBitVector1()
{A::vector<int> v1;A::vector<int> v2(10, 5);int array[] = { 1,2,3,4,5 };A::vector<int> v3(array, array + sizeof(array) / sizeof(array[0]));A::vector<int> v4(v3);for (size_t i = 0; i < v2.size(); ++i){cout << v2[i] << " ";}cout << endl;auto it = v3.begin();while (it != v3.end()){cout << *it << " ";++it;}cout << endl;for (auto e : v4){cout << e << " ";}cout << endl;
}void TestBitVector2()
{A::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);cout << v.size() << endl;cout << v.capacity() << endl;cout << v.front() << endl;cout << v.back() << endl;cout << v[0] << endl;for (auto e : v){cout << e << " ";}cout << endl;v.pop_back();v.pop_back();for (auto e : v){cout << e << " ";}cout << endl;v.insert(v.begin(), 0);for (auto e : v){cout << e << " ";}cout << endl;v.erase(v.begin() + 1);for (auto e : v){cout << e << " ";}cout << endl;
}

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

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

相关文章

stm32-定时器输入捕获

目录 一、输入捕获简介 二、输入捕获框图 1.定时器总框图 2.输入捕获框图 3.主从触发模式 三、固件库实现 1.定时器测量PWM频率 2.PWMI模式 一、输入捕获简介 二、输入捕获框图 1.定时器总框图 上图可知&#xff0c;四个输入捕获和输出比较共用4个CCR寄存器&#x…

lftp服务与http服务(包含scp服务)详解

目录 前言: 1.lftp服务 1.1lftp服务的介绍以及应用场景 1.2安装lftp服务 1.2进行配置 1.3实际操作 2.http服务 2.1http服务介绍以及应用场景 2.1安装httpd服务 2.2进行配置 2.3实际操作 3.scp服务 3.1scp服务的介绍以及应用场景 致谢: 前言: 在当今互联网…

Seata 2.x 系列【12】高可用集群部署

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Seata 版本 2.0.0 本系列Spring Boot 版本 3.2.0 本系列Spring Cloud 版本 2023.0.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-seata-demo 文章目录 1. 概述2. 搭建演…

Unity AI Navigation插件快速使用方法

AI Navigation插件使您能够创建能够在游戏世界中智能移动的角色。这些角色利用的是根据场景几何结构自动生成的导航网格。障碍物可以让您在运行时改变角色的导航路径。 演示使用的Unity版本为Tuanjie 1.0.0,团结引擎是Unity中国的引擎研发团队基于Unity 2022 LTS版本为中国开发…

java学习之路-程序逻辑控制

目录 1.分支结构 1.1 if语句 栗子 判断奇数还是偶数 判断一个年份是否为闰年 1.2switch语句 栗子 2. 循环结构 2.1while 循环 栗子 2.2break和continue break continue 2.3for循环 基本语法 栗子 2.4 do while 循环 3.输入输出 3.1输出 3.2从键盘输入 栗子…

DayDreamInGIS 之 ArcGIS Pro二次开发 锐角检查

功能&#xff1a;检查图斑中所有的夹角&#xff0c;如果为锐角&#xff0c;在单独的标记图层中标记。生成的结果放在默认gdb中&#xff0c;以 图层名_锐角检查 的方式命名 大体实现方式&#xff1a;遍历图层中的所有要素&#xff08;多部件要素分别处理&#xff09;&#xff0…

【SQL Server】实验七 数据完整性

1 实验目的 掌握实体完整性、参照完整性和用户自定义完整性约束的创建方法。掌握完整性约束的运行检查机制。掌握参照完整性的级联删除和修改方法。掌握正确设计关系模式完整性约束的方法。 2 实验内容 2.1 掌握实体完整性约束的创建和使用方法 创建表时定义由一个属性组成…

最好用的流程编辑器bpmn-js系列之基本使用

BPMN&#xff08;Business Process Modeling Notation&#xff09;是由业务流程管理倡议组织BPMI&#xff08;The Business Process Management Initiative&#xff09;开发的一套标准的业务流程建模符号规范。其目的是为用户提供一套容易理解的标准符号&#xff0c;这些符号作…

C++笔记:从零开始一步步手撕高阶数据结构AVL树

文章目录 高度平衡二叉搜索树实现一颗AVL树结点与树的描述——定义类AVL树的插入操作步骤1&#xff1a;按照二叉搜索树的方法插入结点步骤2&#xff1a;自底向上调整平衡因子步骤3&#xff1a;触发旋转操作&#xff08;AVL树平衡的精髓&#xff09;右单旋左单旋左右双旋右左双旋…

基于openresty构建运维工具链实践

本文字数&#xff1a;4591字 预计阅读时间&#xff1a;25 01 导读 如今OpenResty已广泛被各个互联网公司在实际生产环境中应用&#xff0c;在保留Nginx高并发、高稳定等特性基础上&#xff0c;通过嵌入Lua来提升在负载均衡层的开发效率并保证其高性能。本文主要介绍接口鉴权、流…

Spring Cloud Alibab 入门搭建,包含Nacos中心,注册服务发现服务,Feign请求,GateWay网关,sentinel限流

源码在最后 一、安装Nacos注册中心 1.1查看Nacos官网&#xff0c;安装Nacos服务&#xff0c;下载源码或者安装包 1.2启动服务&#xff0c;默认端口为8848&#xff0c; 二、创建服务注册&发现 2.1使用脚手架&#xff0c;创建注册服务和发现服务项目&#xff0c;我用的版…

.NET开源快速、强大、免费的电子表格组件

今天大姚给大家分享一个.NET开源&#xff08;MIT License&#xff09;、快速、强大、免费的电子表格组件&#xff0c;支持数据格式、冻结、大纲、公式计算、图表、脚本执行等。兼容 Excel 2007 (.xlsx) 格式&#xff0c;支持WinForm、WPF和Android平台&#xff1a;ReoGrid。 项…

【机器学习300问】34、决策树对于数值型特征如果确定阈值?

还是用之前的猫狗二分类任务举例&#xff08;这个例子出现在【机器学习300问】第33问中&#xff09;&#xff0c;我们新增一个数值型特征&#xff08;体重&#xff09;&#xff0c;下表是数据集的详情。如果想了解更多决策树的知识可以看看我之前的两篇文章&#xff1a; 【机器…

Linux从0到1——Linux第一个小程序:进度条

Linux从0到1——Linux第一个小程序&#xff1a;进度条 1. 输出缓冲区2. 回车和换行的本质3. 实现进度条3.1 简单原理版本3.2 实际工程版本 1. 输出缓冲区 1. 小实验&#xff1a; 编写一个test.c文件&#xff0c;&#xff1a; #include <stdio.h> #include <unistd.h…

详解命令docker run -d --name container_name -e TZ=Asia/Shanghai your_image

docker run 是Docker的主要命令&#xff0c;用于从镜像启动一个新的容器。下面详细解释并举例说明 -d, --name, -e TZ 参数的用法&#xff1a; -d 或 --detach&#xff1a; 这个标志告诉Docker以守护进程&#xff08;后台&#xff09;模式运行容器。这意味着当你执行 docker ru…

学习笔记-华为IPD转型2020:1,IPD的重要意义

华为产品开发转型&#xff1a;IPD计划 大多数公司发现&#xff0c;当公司大幅增长时&#xff0c;在较小规模上有效的管理实践不再有效。产品开发过程也是如此。随着华为的发展&#xff0c;该公司遇到了产品故障率更高、开发周期更长和研发成本增加等问题。然后&#xff0c;它转…

Lord 3DMCV7-AHRS 时间同步硬件触发设置

目的:通过FPGA发送脉冲触发IMU采集数据。FPGA发送脉冲时,IMU才有数据产生。 FPGA与IMU的硬件接线就不讲了,这里主要说明的是IMU的设置以及ROS驱动的config文件更改。 1. WIN上位机设置 通过IMU在WINDOWS的上位机SensorConnect对IMU的GPIO、波特率等基本功能进行设值,具体…

Unity PS5开发 天坑篇 之 申请开发者与硬件部署01

腾了好几天终于把PS5开发机调试部署成功, 希望能帮到国内的开发者, 主机游戏PlayStation/Nintendo Switch都是比较闭塞的&#xff0c;开发者账号是必须的。 开发环境有两个部分&#xff0c;一是DEV Kit 开发机, TEST Kit测试机两部分组成&#xff0c;二是Unity的支持库(安装后…

vue使用elementPlus ui框架,如何给Dialog 对话框添加Loading 自定义类名显示隐藏

vue使用elementPlus ui框架时&#xff0c;如何给Dialog 对话框添加Loading 自定义类名&#xff0c;想要实现dialog对话框区域有loading效果 官方给出的这个API配置项customClass&#xff0c;使用不太明确。暂时无法实现绑定class。 最后的实现方式&#xff1a; <template&…

3、设计模式之工厂模式2(Factory)

一、什么是工厂模式 工厂模式属于创建型设计模式&#xff0c;它用于解耦对象的创建和使用。通常情况下&#xff0c;我们创建对象时需要使用new操作符&#xff0c;但是使用new操作符创建对象会使代码具有耦合性。工厂模式通过提供一个公共的接口&#xff0c;使得我们可以在不暴露…