Vector 动态数组(迭代器)

news2024/11/29 3:50:08

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)
{
    assert(start != nullptr && end != nullptr);
    m_capacity = m_size = ((size_t)end - (size_t)start) / sizeof(int);//这里如果用int来存放可能会盛不下,size_t可以保证盛放的下
    assert(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 ints
    assert(first.empty() == true && first.size() == 0);
    Vector second(4, 100);                       // four ints with value 100
    assert(second.empty() == false);
    assert(second.size() == 4);
    assert(*second.begin() == 100);
    Vector fourth(second);                       // a copy of third
    assert(fourth.size() == second.size());

    int myints[] = { 16,2,77,29 };
    Vector fifth(myints, myints + sizeof(myints) / sizeof(int));
    assert(fifth.empty() == false);
    assert(fifth[0] == 16);
    assert(fifth[3] == 29);
    assert(fifth.size() == sizeof(myints) / sizeof(int));
    print(fifth, "fifth");//The contents of fifth are:16 2 77 29 
    fifth.push_back(30);
    assert(fifth[4] == 30);
    assert(fifth.size() == 5);
    print(fifth, "fifth");//The contents of fifth are:16 2 77 29 30 
    assert(fifth.size() == sizeof(myints) / sizeof(int) + 1);
    first = fifth = fifth;
    print(first, "first");//The contents of first are:16 2 77 29 30 
    assert(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);
        assert(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;
    }
    assert(a1.size() == sizeof(myints) / sizeof(int));
    {
        Vector c;
        c = fifth;
        c[0] = 1;
        assert(c[0] == 1);
    }
}

期待输出:

祝你好运!

答案在此

C++数据结构与算法(全部答案)_数据结构与算法c++版答案_C++开发者的博客-CSDN博客

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

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

相关文章

3分钟上手Python的命令行参数解析

1 简介 处理命令行参数对许多程序来说都是必不可少的功能。今天为大家介绍 Python 中的 argparse 模块&#xff0c;它是 Python 标准库中的一个模块&#xff0c;它可以让开发者非常简单地为程序添加命令行参数接口&#xff0c;包括位置参数、可选参数、标志等&#xff0c;并自…

测试左移——代码审计SonarQube 平台搭建

一、sonarqube代码分析技术体系 1、代码分析工具 IDE 辅助功能 xcode、android studio阿里巴巴 java 开发手册 ide 插件支持 独立的静态分析工具 spotbugs、findbugs、androidlint、scan-build、Checkstyle、FindSecBugspmd 阿里巴巴 java 开发手册 pmd 插件 综合性的代码…

说说FLINK细粒度滑动窗口如何处理

分析&回答 Flink的窗口机制是其底层核心之一&#xff0c;也是高效流处理的关键。Flink窗口分配的基类是WindowAssigner抽象类&#xff0c;下面的类图示出了Flink能够提供的所有窗口类型。 Flink窗口分为滚动&#xff08;tumbling&#xff09;、滑动&#xff08;sliding&am…

DWA算法学习

一、DWA概念  DWA(动态窗口法)属于局部路径规划方法&#xff0c;为ROS中主要采用的方法。其原理主要是在速度空间&#xff08;v,w&#xff09;中采样多组速度&#xff0c;并模拟这些速度在一定时间内的运动轨迹&#xff0c;再通过一个评价函数对这些轨迹打分&#xff0c;最优的…

2023年全国职业院校技能大赛网络系统管理赛项 模块B:服务部署 卷II

2023年全国职业院校技能大赛 GZ073网络系统管理赛项 模块B&#xff1a;服务部署 卷II 目 录 一、Windows项目任务描述 1 &#xff08;一&#xff09;拓扑图 1 &#xff08;二&#xff09;网络地址规划 1 二、Windows项目任务清单 2 &#xff08;一&#xff09;服务器IspSrver…

数据库(一) 基础知识

概述 数据库是按照数据结构来组织,存储和管理数据的仓库 数据模型 数据库系统的核心和基础是数据模型&#xff0c;数据模型是严格定义的一组概念的集合。因此数据模型一般由数据结构、数据操作和完整性约束三部分组成。数据模型主要分为三种:层次模型&#xff0c;网状模型和关…

【云原生】Ansible自动化批量操作工具playbook剧本

目录 1.playbook相关知识 1.1 playbook 的简介 1.2 playbook的 各部分组成 2. 基础的playbook剧本编写实例 2.1 playbook编写Apache安装剧本&#xff08;yum方式安装&#xff09; 报错集&#xff1a; 实例2&#xff1a;playbook编写nginx 的yum安装并且能修改其监听端口的…

QT基础教程之四QMainWindow

QT基础教程之四QMainWindow QMainWindow是一个为用户提供主窗口程序的类&#xff0c;包含一个菜单栏&#xff08;menu bar&#xff09;、多个工具栏(tool bars)、多个锚接部件(dock widgets)、一个状态栏(status bar)及一个中心部件(central widget)&#xff0c;是许多应用程序…

13、监测数据采集物联网应用开发步骤(9.2)

监测数据采集物联网应用开发步骤(9.1) TCP/IP Server开发 新建TCP/IP Server线程类com.zxy.tcp.ServerThread.py #! python3 # -*- coding: utf-8 -Created on 2017年05月10日 author: zxyong 13738196011 import socket,threading,time from com.zxy.tcp.TcpServer import …

业务流程与逻辑编排的低代码平台,一文全方位了解它的轻应用信息

JVS低代码开发平台提供了大量的可配置组件和预先集成的功能&#xff0c;开发人员可以通过拖拽和设置属性的方式&#xff0c;快速搭建应用程序的前端界面和交互逻辑。同时&#xff0c;低代码平台也提供了丰富的后端服务和集成能力&#xff0c;可以轻松地与现有的系统和第三方服务…

vulnhub靶机02-Breakout

主机发现 arp-scan -l 扫描端口 nmap --min-rate 10000 -p- 192.168.21.143 扫描端口信息 nmap -sV -sT -O -p80,139,445,10000,20000 192.168.21.143 漏洞扫描 nmap --scriptvuln -p80,139,445,10000,20000 192.168.21.143 先看网站 什么都没有看看f12 找到点好东西 解码…

被遗弃的多重继承

问题 C 是否允许一个类继承自多个父类&#xff1f; C 支持编写多重继承的代码 一个子类可以拥有多个父类 子类拥有所有父类的成员变量 子类继承所有父类的成员函数 子类对象可以当作任意父类对象使用 多重继承的语法规则 多重继承的本质与单继承相同&#xff01; 通过多重…

ChatRWKV 学习笔记和使用指南

0x0. 前言 Receptance Weighted Key Value&#xff08;RWKV&#xff09;是pengbo提出的一个新的语言模型架构&#xff0c;它使用了线性的注意力机制&#xff0c;把Transformer的高效并行训练与RNN的高效推理相结合&#xff0c;使得模型在训练期间可以并行&#xff0c;并在推理…

基于Java的代驾管理系统 springboot+vue,mysql数据库,前台用户、商户+后台管理员,有一万五千字报告,完美运行

基于Java的代驾管理系统 springbootvue&#xff0c;mysql数据库&#xff0c;前台用户、商户后台管理员&#xff0c;有一万五千字报告&#xff0c;完美运行。 系统完美实现用户下单叫车、商户接单、管理员管理系统&#xff0c;页面良好&#xff0c;系统流畅。 各角色功能&#x…

GPT能否辅助数学学习

GPT4.0的数学能力怎么样&#xff1f;我们使用镜像站进行实验&#xff0c;通过不同水平的数学看看GPT4.0的数学能力得到进步没有。镜像站的地址我们放在了最后&#xff0c;各位读者也可以自行去测试。 笔者在ChatGPT镜像站进行测试&#xff0c;我们的实验是让GPT4.0自行出数学题…

记本地新建一个gradle方式springboot项目过程

打算使用gradle在idea新建个springboot项目&#xff0c;然后坑很多&#xff0c;记录一下 原来我的idea应该是社区版&#xff0c;新建项目时候没有可以选择spring相关配置&#xff0c;然后卸载了重装&#xff0c;之前问题是启动是启动起来了&#xff0c;但是状态栏那边一直显示…

招投标系统简介 企业电子招投标采购系统源码之电子招投标系统 —降低企业采购成本

功能模块&#xff1a; 待办消息&#xff0c;招标公告&#xff0c;中标公告&#xff0c;信息发布 描述&#xff1a; 全过程数字化采购管理&#xff0c;打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力&#xff0c;为外部…

TCP连接分析:探寻TCP的三次握手

文章目录 一、实验背景与目的二、实验需求三、实验解法1. 预先抓包监测使用Wireshark工具2.进行TCP三次握手&#xff0c;访问www.baidu.com3.分析Wireshark捕获的TCP包 摘要&#xff1a; 本实验使用Wireshark工具&#xff0c;通过抓包监测和分析&#xff0c;深入研究了与百度服…

Richtek立锜EPS助力转向系统方案

Richtek立锜EPS助力转向系统方案包含一个集成的位置控制模块&#xff0c;用来接收一个外部系统&#xff08;泊车模块&#xff09;的角度请求以实现控制EPS系统自动转向的功能。外部交互接口用作与外部泊车模块进行CAN通讯&#xff0c;以支持泊车模块的控制状态的切换和输入角度…

nextTick原理

nextTick 是 Vue 提供的一个异步方法&#xff0c;用于在 DOM 更新之后执行回调函数。它的原理是利用 JavaScript 的事件循环机制来实现异步执行。 具体来说&#xff0c;当我们调用 nextTick 方法时&#xff0c;Vue 会将传入的回调函数添加到一个队列中。在下一个事件循环中&am…