2.3 Vector 动态数组(迭代器)

C++数据结构与算法 目录

本文前驱课程

1 C++自学精简教程 目录(必读)

2 Vector<T> 动态数组(模板语法)

本文目标

1 熟悉迭代器设计模式;

2 实现数组的迭代器;

3 基于迭代器的容器遍历;

迭代器语法介绍

对迭代器的详细介绍参考 : 迭代器 iterator 范围for循环 删除容器的元素 remove erase

迭代器的能力

迭代器的功能

迭代器实际上是一个内部类。通过下面的迷你代码,我们可以看到迭代器应该具备的能力。

class Vector
{
public:class Iterator{};Iterator begin() {Iterator itr;/* (1)开始迭代器要能指向第一个元素 m_data[0]*/return itr;};Iterator end(){Iterator itr;/* (2)结束迭代器指向空最后一个元素的下一个位置 m_data+m_size */return itr;};int& operator*();// 重载解引用操作符重载:让迭代器可以通过解引用得到其指向变量的引用  *itr = 5;iterator & operator++(); //用于前置形式  ++itr;int* m_data;//存储动态内存int m_size;
};int main()
{Vector arr;auto itr = arr.begin();for (auto itr = arr.begin(); itr != arr.end()/* (3)迭代器要能够比较相等*/; itr++/* (4)迭代器要能够移动到下一个位置 */ ){cout << *itr/* (5)迭代器要能够解引用得到容器的元素*/ << " ";}return 0;
}

迭代器的实现

现在我们考虑如何实现这种能力。

对于动态数组Vector来说:

(1)开始迭代器要能指向第一个元素 m_data[0]:可以给迭代器添加一个构造函数,传递动态数组的首元素地址给迭代器。

(2)结束迭代器指向空最后一个元素的下一个位置:可以给迭代器的构造函数传入动态数组首地址偏移 m_size 个元素后的地址。m_data + m_size

(3)迭代器要能够比较相等:让迭代器重载, 等于操作符 == 不等于操作符 !=

(4)迭代器要能够移动到下一个位置:让迭代器重载自增操作符 ++

(5)迭代器要能够解引用得到容器的元素:让迭代器重载解引用操作符 *

至此,迭代器的功能就全部实现完成了。

完整测试用例

//------下面的代码是用来测试你的代码有没有问题的辅助代码,你无需关注------
#include <algorithm>
#include <cstdlib>
#include <iostream> 
#include <vector>
#include <utility>
using namespace std;
struct Record { Record(void* ptr1, size_t count1, const char* location1, int line1, bool is) :ptr(ptr1), count(count1), line(line1), is_array(is) { int i = 0; while ((location[i] = location1[i]) && i < 100) { ++i; } }void* ptr; size_t count; char location[100] = { 0 }; int line; bool is_array = false; bool not_use_right_delete = false; }; bool operator==(const Record& lhs, const Record& rhs) { return lhs.ptr == rhs.ptr; }std::vector<Record> myAllocStatistic; void* newFunctionImpl(std::size_t sz, char const* file, int line, bool is) { void* ptr = std::malloc(sz); myAllocStatistic.push_back({ ptr,sz, file, line , is }); return ptr; }void* operator new(std::size_t sz, char const* file, int line) { return newFunctionImpl(sz, file, line, false); }void* operator new [](std::size_t sz, char const* file, int line)
{ return newFunctionImpl(sz, file, line, true); }void operator delete(void* ptr) noexcept { Record item{ ptr, 0, "", 0, false }; auto itr = std::find(myAllocStatistic.begin(), myAllocStatistic.end(), item); if (itr != myAllocStatistic.end()) { auto ind = std::distance(myAllocStatistic.begin(), itr); myAllocStatistic[ind].ptr = nullptr; if (itr->is_array) { myAllocStatistic[ind].not_use_right_delete = true; } else { myAllocStatistic[ind].count = 0; }std::free(ptr); } }void operator delete[](void* ptr) noexcept {Record item{ ptr, 0, "", 0, true }; auto itr = std::find(myAllocStatistic.begin(), myAllocStatistic.end(), item); if (itr != myAllocStatistic.end()) { auto ind = std::distance(myAllocStatistic.begin(), itr); myAllocStatistic[ind].ptr = nullptr; if (!itr->is_array) { myAllocStatistic[ind].not_use_right_delete = true; } else { myAllocStatistic[ind].count = 0; }std::free(ptr); }}
#define new new(__FILE__, __LINE__)
struct MyStruct { void ReportMemoryLeak() { std::cout << "Memory leak report: " << std::endl; bool leak = false; for (auto& i : myAllocStatistic) { if (i.count != 0) { leak = true; std::cout << "leak count " << i.count << " Byte" << ", file " << i.location << ", line " << i.line; if (i.not_use_right_delete) { cout << ", not use right delete. "; }	cout << std::endl; } }if (!leak) { cout << "No memory leak." << endl; } }~MyStruct() { ReportMemoryLeak(); } }; static MyStruct my; void check_do(bool b, int line = __LINE__) { if (b) { cout << "line:" << line << " Pass" << endl; } else { cout << "line:" << line << " Ohh! not passed!!!!!!!!!!!!!!!!!!!!!!!!!!!" << " " << endl; exit(0); } }
#define check(msg)  check_do(msg, __LINE__);
//------上面的代码是用来测试你的代码有没有问题的辅助代码,你无需关注------#include <iostream>
#include <cassert>class Vector
{
public:Vector(void);//1 默认构造函数Vector(int count, int value);//2 非默认构造函数Vector(const Vector& from);//4 复制构造函数Vector(int* start, int* end);// 3 非默认构造函数Vector& operator = (const Vector& from);~Vector();
public:size_t size(void) const;bool empty(void) const;const int& operator[] (size_t n) const;int& operator[] (size_t n);void push_back(const int& val);
public:class iterator{friend class  Vector;friend bool operator == (const iterator& lhs, const iterator& rhs);//用于实现!=,因为==非常容易实现friend bool operator != (const iterator& lhs, const iterator& rhs);public:iterator& operator++(); //用于前置形式iterator operator++(int); //用于后置形式,这里有个int参数纯粹是为了区分前自增操作符而加的语法规定int& operator*();//解引用操作符重载private:int* m_hold = nullptr;};class const_iterator{friend class  Vector;public:bool operator == (const const_iterator& rhs) { return this->m_hold == rhs.m_hold; }//用于实现!=,因为==非常容易实现bool operator != (const const_iterator& rhs) { return !(*this == rhs); };const_iterator& operator++(); //用于前置形式 ++itr  先改变自己,指向下一个位置,再返回自己const_iterator operator++(int); //用于后置形式 itr++ 先创建一个改变前的副本用于返回,再在返回前改变自己,指向下一个位置const int& operator*() const;private:const int* m_hold = nullptr;};//noexcept 表示这个函数内不会抛出异常,这样有助于编译器优化代码生成const_iterator begin() const noexcept;iterator begin() noexcept;const_iterator end() const noexcept;iterator end() noexcept;
private:void clear(void);
private:size_t m_size;//当前元素数量size_t m_capacity;//容量int* m_data;//数据部分
};
Vector::Vector(void):m_data(nullptr), m_size(0), m_capacity(0)
{std::cout << "Vector()" << std::endl;
}Vector::Vector(const Vector& from)
{if (from.empty()){m_data = nullptr;m_size = 0;m_capacity = 0;return;}m_capacity = m_size = from.m_size;m_data = new int[m_size];for (auto i = 0; i < m_size; ++i){m_data[i] = from.m_data[i];}std::cout << "Vector(const Vector & from)" << std::endl;
}Vector::Vector(int count, int value): m_data(nullptr)
{if (count <= 0){throw std::runtime_error("size of vector to init must bigger than zero!");}m_data = new int[count];for (size_t i = 0; i < count; i++){m_data[i] = value;}m_capacity = m_size = count;std::cout << "Vector(const, value)" << std::endl;
}Vector::Vector(int* start, int* end): m_data(nullptr), m_size(0), m_capacity(0)
{check(start != nullptr && end != nullptr);m_capacity = m_size = ((size_t)end - (size_t)start) / sizeof(int);//这里如果用int来存放可能会盛不下,size_t可以保证盛放的下check(m_size > 0);m_data = new int[m_size];for (size_t i = 0; i < m_size; i++){m_data[i] = *start++;}std::cout << "Vector(start, end)" << std::endl;
}Vector& Vector::operator=(const Vector& from)
{if (this == &from){return *this;}//先释放自己的数据clear();m_size = from.m_size;m_capacity = from.m_capacity;m_data = new int[m_size];for (size_t i = 0; i < m_size; i++){m_data[i] = from.m_data[i];}return *this;std::cout << "Vector & Vector::operator=(const Vector & from)" << std::endl;
}Vector::~Vector()
{if (m_data){delete[] m_data;}std::cout << "~Vector()" << std::endl;
}size_t Vector::size(void) const
{return m_size;
}bool Vector::empty(void) const
{return m_size == 0;
}const int& Vector::operator[](size_t n) const
{return m_data[n];
}int& Vector::operator[](size_t n)
{return  m_data[n];
}void Vector::push_back(const int& val)
{if (m_capacity > m_size)//直接追加到最后一个{m_data[m_size++] = val;}else//只有满了的那一瞬间,才翻倍开辟新空间{auto pNewArray = new int[m_capacity = m_capacity + m_capacity];//拷贝老数据for (size_t i = 0; i < m_size; i++){pNewArray[i] = m_data[i];}//追加最新的末尾元素pNewArray[m_size++] = val;delete[] m_data;m_data = pNewArray;}
}
//下面的代码 函数名是 Vector::begin
//           返回值类型是 Vector::const_iterator
//返回值类型之所以要加类作用域是因为,返回值类型在函数作用域之外。这是由C语言继承而来的
Vector::const_iterator Vector::begin() const noexcept
{if (empty()){return end();}const_iterator itr;//(1) your code 下面的代码仅仅是让编译通过,可能需要你重新实现return itr;
}Vector::iterator Vector::begin() noexcept
{if (empty()){return end();}iterator itr;//(1) your code 下面的代码仅仅是让编译通过,可能需要你重新实现return itr;
}Vector::const_iterator Vector::end() const noexcept
{const_iterator itr;//(2) your code 下面的代码仅仅是让编译通过,可能需要你重新实现// 如果容器为空,不能返回下标返回的元素位置return itr;
}Vector::iterator Vector::end() noexcept
{iterator itr;//(2) your code 下面的代码仅仅是让编译通过,可能需要你重新实现// 如果容器为空,不能返回下标返回的元素位置return itr;
}void Vector::clear(void)
{//(3) your code 下面的代码仅仅是让编译通过,可能需要你重新实现}bool operator==(const Vector::iterator& lhs, const Vector::iterator& rhs)
{//(4) your code 下面的代码仅仅是让编译通过,可能需要你重新实现return false;
}bool operator!=(const Vector::iterator& lhs, const Vector::iterator& rhs)
{//(5) your code 下面的代码仅仅是让编译通过,可能需要你重新实现return false;
}Vector::iterator& Vector::iterator::operator++()
{//(6) your code 下面的代码仅仅是让编译通过,可能需要你重新实现return *this;
}
Vector::const_iterator& Vector::const_iterator::operator++()
{//(7) your code 下面的代码仅仅是让编译通过,可能需要你重新实现return *this;
}Vector::iterator Vector::iterator::operator++(int)
{//(8) your code 下面的代码仅仅是让编译通过,可能需要你重新实现return iterator();
}
Vector::const_iterator Vector::const_iterator::operator++(int)
{return Vector::const_iterator();
}
int& Vector::iterator::operator*()
{//(9) your code 下面的代码是错误的!不可以返回临时变量的引用!仅仅是让编译通过,需要你重新实现int a = 0;return a;
}
const int& Vector::const_iterator::operator*() const
{//(9) your code 下面的代码是错误的!不可以返回临时变量的引用!仅仅是让编译通过,需要你重新实现int a = 0;return a;
}
void print(const Vector& v, const std::string& msg)
{std::cout << "print The contents of " << msg.c_str() << " are:";for (int i = 0; i < v.size(); ++i){std::cout << ' ' << v[i];}std::cout << '\n';
}
void print_itr(Vector& v, const std::string& msg)
{std::cout << "print_itr The contents of " << msg.c_str() << " are:";for (auto itr = v.begin(); itr != v.end(); ++itr){std::cout << ' ' << *itr;}std::cout << '\n';
}
void print_const_itr(const Vector& v, const std::string& msg)
{std::cout << "print_const_itr The contents of " << msg.c_str() << " are:";for (auto itr = v.begin(); itr != v.end(); ++itr){//*itr = 4;std::cout << ' ' << *itr;}std::cout << '\n';
}int main()
{Vector a;Vector first;                   // empty vector of intscheck(first.empty() == true && first.size() == 0);Vector second(4, 100);                       // four ints with value 100check(second.empty() == false);check(second.size() == 4);check(*second.begin() == 100);Vector fourth(second);                       // a copy of thirdcheck(fourth.size() == second.size());int myints[] = { 16,2,77,29 };Vector fifth(myints, myints + sizeof(myints) / sizeof(int));check(fifth.empty() == false);check(fifth[0] == 16);check(fifth[3] == 29);check(fifth.size() == sizeof(myints) / sizeof(int));print(fifth, "fifth");//The contents of fifth are:16 2 77 29 fifth.push_back(30);check(fifth[4] == 30);check(fifth.size() == 5);print(fifth, "fifth");//The contents of fifth are:16 2 77 29 30 check(fifth.size() == sizeof(myints) / sizeof(int) + 1);first = fifth = fifth;print(first, "first");//The contents of first are:16 2 77 29 30 check(first.empty() == false && first.size() == fifth.size());print_itr(fifth, "fifth");//The contents of fifth are:16 2 77 29 30 print_const_itr(fifth, "fifth");//The contents of fifth are:16 2 77 29 30 Vector a1(myints, myints + sizeof(myints) / sizeof(int));{Vector b(a1);b.push_back(2);check(b[4] == 2);}{Vector c;for (auto i : c){std::cout << i << " ";}c = a1;a1 = c;c = a1;for (auto i : c){std::cout << i << " ";}std::cout << std::endl;}check(a1.size() == sizeof(myints) / sizeof(int));{Vector c;c = fifth;c[0] = 1;check(c[0] == 1);}
}

期待输出:

Vector()
Vector()
line:335 Pass
Vector(const, value)
line:337 Pass
line:338 Pass
line:339 Pass
Vector(const Vector & from)
line:341 Pass
line:118 Pass
line:120 Pass
Vector(start, end)
line:345 Pass
line:346 Pass
line:347 Pass
line:348 Pass
print The contents of fifth are: 16 2 77 29
line:351 Pass
line:352 Pass
print The contents of fifth are: 16 2 77 29 30
line:354 Pass
print The contents of first are: 16 2 77 29 30
line:357 Pass
print_itr The contents of fifth are: 16 2 77 29 30
print_const_itr The contents of fifth are: 16 2 77 29 30
line:118 Pass
line:120 Pass
Vector(start, end)
Vector(const Vector & from)
line:364 Pass
~Vector()
Vector()
16 2 77 29
~Vector()
line:382 Pass
Vector()
line:387 Pass
~Vector()
~Vector()
~Vector()
~Vector()
~Vector()
~Vector()
~Vector()
Memory leak report:
No memory leak.

祝你好运!

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

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

相关文章

全民健康生活方式行动日,天猫健康联合三诺生物推出“15天持续测糖计划”

糖尿病是全球高发慢性病中患病人数增长最快的疾病&#xff0c;是导致心血管疾病、失明、肾衰竭以及截肢等重大疾病的主要病因之一。目前中国有近1.4亿成人糖尿病患者&#xff0c;科学的血糖监测和健康管理对于糖尿病患者来说至关重要。 在9月1日全民健康生活方式行动日前夕&am…

如何排查 Flink Checkpoint 失败问题?

分析&回答 这是 Flink 相关工作中最常出现的问题&#xff0c;值得大家搞明白。 1. 先找到超时的subtask序号 图有点问题&#xff0c;因为都是成功没失败的&#xff0c;尴尬了。 借图&#xff1a; 2. 找到对应的机器和任务 方法很多&#xff0c;这里看自己习惯和公司提供…

【网络编程上】

目录 一.什么是互联网 1.计算机网络的定义与分类&#xff08;了解&#xff09; &#xff08;1&#xff09;计算机网络的定义 &#xff08;2&#xff09;计算机网络的分类 ① 按照网络的作用范围进行分类 ②按照网络的使用者进行分类 2.网络的网络 &#xff08;理解&#xf…

探索内网穿透工具:实现局域网SQL Server数据库的公网远程访问方法

文章目录 1.前言2.本地安装和设置SQL Server2.1 SQL Server下载2.2 SQL Server本地连接测试2.3 Cpolar内网穿透的下载和安装2.3 Cpolar内网穿透的注册 3.本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4.公网访问测试5.结语 1.前言 数据库的重要性相信大家都有所了解&…

【UE 材质】常用向量运算节点——点积、叉积、归一化

目录 一、点积 二、叉积 三、归一化 一、点积 点积&#xff0c;也称为内积或数量积&#xff0c;是一种用于计算两个向量之间关系的操作。对于两个三维向量 A&#xff08;a1,a2,a3&#xff09;和 B(b1,b2,b3)&#xff0c;它们的点积可以用以下公式表示&#xff1a; ABa1​⋅…

【设计模式】Head First 设计模式——构建器模式 C++实现

设计模式最大的作用就是在变化和稳定中间寻找隔离点&#xff0c;然后分离它们&#xff0c;从而管理变化。将变化像小兔子一样关到笼子里&#xff0c;让它在笼子里随便跳&#xff0c;而不至于跳出来把你整个房间给污染掉。 设计思想 ​ 将一个复杂对象的构建与其表示相分离&…

实验室的服务器和本地pycharm怎么做图传

提前说一个 自认为 比较重要的一点&#xff1a; 容器中安装opencv&#xff0c;可以先试试用 apt install libopencv-dev python3-opencv 我感觉在图传的时候用的不是 opencv-python 而是ubuntu的opencv库 所以用 apt install 安装试一下 参考 远程调试 qt.qpa.xcb: coul…

大数据Flink(七十一):SQL的时间属性

文章目录 SQL的时间属性 一、Flink三种时间属性简介

windows11 利用vmware17 安装rocky9操作系统

下载相关软件和镜像 vmware17 下载 下载页面 Download VMware Workstation Pro ​ rocky8镜像下载 官网链接&#xff1a;Rocky Linux 下载页面 Download Rocky | Rocky Linux 点击Minimal下载 安装rocky9 选择镜像文件&#xff0c;点击下一步 点击下一步 启动虚拟机 选…

程序开发:构建功能强大的应用的艺术

程序开发是在今天的数字化时代中扮演重要角色的一项技术。通过编写代码&#xff0c;开发人员能创造出无数不同的应用&#xff0c;从简单的计算器到复杂的社交平台。电子商务应用、在线教育平台、医疗记录系统等&#xff0c;都重视程序开发的重要性&#xff0c;通过这其中的交互…

xml和json互转工具类

分享一个json与xml互转的工具类&#xff0c;非常好用 一、maven依赖 <!-->json 和 xm 互转</!--><dependency><groupId>org.dom4j</groupId><artifactId>dom4j</artifactId><version>2.1.3</version></dependency&g…

深度学习推荐系统(二)Deep Crossing及其在Criteo数据集上的应用

深度学习推荐系统(二)Deep Crossing及其在Criteo数据集上的应用 在2016年&#xff0c; 随着微软的Deep Crossing&#xff0c; 谷歌的Wide&Deep以及FNN、PNN等一大批优秀的深度学习模型被提出&#xff0c; 推荐系统全面进入了深度学习时代&#xff0c; 时至今日&#xff0c…

说说Flink on yarn的启动流程

分析&回答 核心流程 FlinkYarnSessionCli 启动的过程中首先会检查Yarn上有没有足够的资源去启动所需要的container&#xff0c;如果有&#xff0c;则上传一些flink的jar和配置文件到HDFS&#xff0c;这里主要是启动AM进程和TaskManager进程的相关依赖jar包和配置文件。接着…

git的常用命令

初始化git&#xff0c;以及如何提交代码 1、配置用户信息 git config --global user.name zhangsan # 设置用户签名 git config --global user.email zhangsanqq.com # 设置用户邮箱&#xff08;不会验证&#xff0c;可以不存在&#xff09;1.1、查看是否已经添加用户配置 在…

k8s 安装 kubernetes安装教程 虚拟机安装k8s centos7安装k8s kuberadmin安装k8s k8s工具安装 k8s安装前配置参数

k8s采用master, node1, node2 。三台虚拟机安装的一主两从&#xff0c;机器已提前安装好docker。下面是机器配置&#xff0c;k8s安装过程&#xff0c;以及出现的问题与解决方法 虚拟机全部采用静态ip, master 30机器, node1 31机器, node2 32机器 机器ip 192.168.164.30 # ma…

protues仿真时有时候串口虚拟中端不弹窗的问题

在使用proteus的时候&#xff0c;有时候你会发现点击调试开始运行后&#xff0c;串口虚拟终端没有自动弹窗的问题&#xff0c;其实照成这种现象的原因是你在使用的过程中移动了器件位置或者是对整个视窗使用鼠标滚动进行缩放了&#xff0c;如果要重新弹窗则需要进行以下操作: …

华为云Stack的学习(四)

五、Service OM资源管理 1.Service OM简介 1.1 Service OM介绍 在华为云Stack解决方案中&#xff0c;Service OM是FusionSphere OpenStack的操作管理界面&#xff0c;是资源池&#xff08;计算、存储、网络&#xff09;以及基础云服务的管理工具。 1.2 Service OM定位 Serv…

【管理运筹学】第 7 章 | 图与网络分析(1,图论背景以及基本概念、术语)

文章目录 引言一、图与网络的基本知识1.1 图与网络的基本概念1.1.1 图的定义1.1.2 图中相关术语1.1.3 一些特殊图类1.1.4 图的运算 写在最后 引言 按照正常进度应该学习动态规划了&#xff0c;但我想换换口味&#xff0c;而且动态规划听说也有一定难度&#xff0c;还不一定会考…

王道考研数据结构

文章目录 C 环境准备官方文档环境准备在线运行VSCode 环境报错解决 绪论线性表顺序表链表错题 栈、队列和数组栈队列栈的应用之中缀转后缀特殊矩阵用数组压缩存储错题 串模式匹配之暴力和KMP 树与二叉树二叉树树和森林哈夫曼树和哈夫曼编码并查集错题 图图的基本概念图的存储及…

windows 2012服务器配置nginx后无法访问域名的问题

环境&#xff1a;Windows 2012 R2 Nginx 问题&#xff1a;确认域名解析到服务器ip已生效&#xff08;通过ping域名地址确认域名已指向该ip&#xff09;&#xff0c;确认nginx配置无误&#xff08;绑定域名、配置端口、配置网站文件目录&#xff09;&#xff0c;但无法从外网访…