【C++】vector 类模拟实现:探索动态数组的奥秘

🌟 快来参与讨论💬,点赞👍、收藏⭐、分享📤,共创活力社区。🌟

如果你对string,vector还存在疑惑,欢迎阅读我之前的作品 : 

之前文章🔥🔥🔥 【C++】vector 类深度解析:探索动态数组的奥秘

               🔥🔥🔥 【C++】string 类深度解析:探秘字符串操作的核心


目录

一、引言🎉

二、vector 类的功能需求分析👀

(一)存储元素数据📦

(二)元素访问与修改✍️

(三)元素数量相关操作📏

(四)迭代器支持🔍

(五)内存管理🧹

三、模拟实现的关键步骤和代码解析💻

(一)类的定义🎯

(二)构造函数实现🔨

(三)析构函数实现🚮

(四)获取元素数量和容量函数📏

(五)判断是否为空函数🤔

(六)预留空间函数📦

(七)调整大小函数📏

(八)下标操作符重载🎯

(九)赋值操作符重载✍️

(十)添加和删除元素函数🎯

(十一)迭代器相关函数🔍

四、总结😎


一、引言🎉

在 C++ 的编程世界里,动态数组就像一个神奇的魔法盒子🎁,可以根据我们的需求灵活地改变大小。而标准库中的 vector 类,就是这个魔法盒子的绝佳实现者🧙‍♂️!它为我们提供了超级方便的操作接口,让我们能够轻松地处理各种动态数据存储和操作的任务。你是不是也像我一样,对 vector 类的内部实现充满了好奇呢😏?

嘿嘿,那就跟着我一起踏上模拟实现 vector 类的奇妙之旅吧🚀! 


二、vector 类的功能需求分析👀

(一)存储元素数据📦

我们希望 vector 类能够像一个智能的容器一样🧺,用连续的内存空间来存放元素。这样的话,就可以像在一排整齐排列的小格子里找东西一样,通过索引快速地访问到任何一个元素😃,是不是很方便呢🧐?而且哦,它还要能自动调整存储空间的大小,就像一个会伸缩的魔法口袋👝,当我们添加或删除元素的时候,它能够自动适应,不会让内存出现浪费或者不够用的情况,是不是超级厉害😎!

(二)元素访问与修改✍️

  1. 当然啦,我们需要能够轻松地通过索引来获取元素的值,就像打开小格子拿东西一样简单😏。而且,我们还希望能够修改这个元素的值,让我们可以随心所欲地更新数据😎。
  2. 有时候,我们想要在指定的位置插入一个新元素,这就好比在排队的人群中突然插入一个小伙伴👫,vector 类要能够巧妙地把后面的元素都往后挪一挪,给新元素腾出空间哦😃。
  3. 还有还有,当我们想要删除某个位置的元素时,vector 类要能像变魔术一样,把后面的元素都往前移一移,填补上这个空缺,保持队伍的整齐有序😎。

(三)元素数量相关操作📏

  1. 我们得知道 vector 里面到底装了多少个元素呀,这样才能更好地控制和处理数据😉。所以,需要有一个方法来获取元素的数量。
  2. 当我们预先知道大概要存储多少个元素的时候,希望能够提前告诉 vector 类,让它提前准备好足够的空间,就像告诉魔法口袋我们大概要装多少东西一样,这样可以提高效率哦😎。

(四)迭代器支持🔍

为了能够方便地遍历 vector 中的元素,我们需要 vector 类提供迭代器。迭代器就像是一个小导游👨‍✈️,可以带着我们逐个访问 vector 中的元素,让遍历操作变得轻松愉快😃。

(五)内存管理🧹

最后但同样重要的是,vector 类要能够合理地管理内存。它要知道什么时候该申请新的内存,什么时候该释放不再使用的内存,就像一个勤劳的小管家一样,把内存管理得井井有条,避免内存泄漏和浪费,确保我们的程序能够高效稳定地运行😎。


三、模拟实现的关键步骤和代码解析💻

(一)类的定义🎯

template<typename T>
class MyVector {
private:
    T* data;           // 存储元素的数组指针
    size_t size_;      // 当前元素数量
    size_t capacity_;  // 数组容量

public:
    MyVector();                            // 默认构造函数
    MyVector(size_t n, const T& value = T());  // 构造函数,初始化n个相同元素
    MyVector(const MyVector<T>& other);     // 拷贝构造函数
    ~MyVector();                           // 析构函数

    size_t size() const;                    // 获取元素数量
    size_t capacity() const;                // 获取数组容量
    bool empty() const;                     // 判断是否为空
    void reserve(size_t new_capacity);      // 预留空间
    void resize(size_t new_size, T value = T());  // 调整大小

    T& operator[](size_t index);            // 下标操作符重载(非const版本)
    const T& operator[](size_t index) const;  // 下标操作符重载(const版本)
    MyVector<T>& operator=(const MyVector<T>& other);  // 赋值操作符重载

    void push_back(const T& value);         // 在末尾添加元素
    void pop_back();                        // 删除末尾元素
    void insert(size_t pos, const T& value);  // 在指定位置插入元素
    void erase(size_t pos);                 // 删除指定位置元素

    typedef T* iterator;                    // 迭代器类型定义
    iterator begin();                       // 返回起始迭代器
    iterator end();                         // 返回结束迭代器
};

 👇解释:

这里我们定义了一个模板类MyVector,它可以存储任意类型的元素哦😎。有一个指针data用来指向存储元素的数组,size_记录当前元素的数量,capacity_表示数组的容量。然后还有各种各样的函数,它们就像一群小助手,分别负责不同的操作任务呢😉。

 

(二)构造函数实现🔨

  1. 默认构造函数
template<typename T>
MyVector<T>::MyVector() : data(nullptr), size_(0), capacity_(0) {}

😃这个默认构造函数超级简单,就像创建一个空的魔法口袋一样,把data指针设为nullptr,表示还没有分配内存,元素数量size_和容量capacity_都初始化为 0😎。
2. 指定大小和初始值的构造函数

template<typename T>
MyVector<T>::MyVector(size_t n, const T& value) : size_(n), capacity_(n) {
    data = new T[capacity_];
    for (size_t i = 0; i < size_; ++i) {
        data[i] = value;
    }
}

👀这个构造函数就像是按照我们的要求定制魔法口袋的大小和初始物品一样🧐。我们告诉它要创建一个能装n个元素的 vector,并且每个元素都初始化为value。它先分配足够的内存空间,然后用一个循环把每个元素都设置为指定的值😉。
3. 拷贝构造函数

template<typename T>
MyVector<T>::MyVector(const MyVector<T>& other) : size_(other.size_), capacity_(other.capacity_) {
    data = new T[capacity_];
    for (size_t i = 0; i < size_; ++i) {
        data[i] = other.data[i];
    }
}

😎拷贝构造函数就像是复制一个一模一样的魔法口袋哦😏。它创建一个新的 vector,大小和容量都和传入的other一样,然后把other中的元素一个一个地复制到新的 vector 中,这样新的 vector 就和原来的一模一样啦😉。

(三)析构函数实现🚮

template<typename T>
MyVector<T>::~MyVector() {
    if (data!= nullptr) {
        delete[] data;
    }
}

👀这个析构函数就像一个勤劳的清洁工🧹,当 vector 对象生命周期结束的时候,它会检查data指针是否为空。如果不为空,就释放掉data所指向的内存,避免内存泄漏,确保我们的程序干干净净、健健康康😎。

(四)获取元素数量和容量函数📏

1.获取元素数量函数

template<typename T>
size_t MyVector<T>::size() const {
    return size_;
}

😃这个函数就像一个小计数器一样,直接返回当前 vector 中元素的数量size_,简单又直接😎。
2. 获取容量函数

template<typename T>
size_t MyVector<T>::capacity() const {
    return capacity_;
}

👀它也是一样的简单,直接把数组的容量capacity_返回给我们,让我们知道这个魔法口袋最多能装多少东西😉。

(五)判断是否为空函数🤔

template<typename T>
bool MyVector<T>::empty() const {
    return size_ == 0;
}

😎这个函数就像一个小侦探🕵️‍♂️,检查元素数量size_是否为 0。如果是 0,那就说明 vector 是空的,就像魔法口袋里什么都没有一样,返回true;否则返回false😉。

(六)预留空间函数📦

template<typename T>
void MyVector<T>::reserve(size_t new_capacity) {
    if (new_capacity > capacity_) {
        T* new_data = new T[new_capacity];
        for (size_t i = 0; i < size_; ++i) {
            new_data[i] = data[i];
        }
        if (data!= nullptr) {
            delete[] data;
        }
        data = new_data;
        capacity_ = new_capacity;
    }
}

👀这个函数就像是给魔法口袋升级扩容一样😃!当我们预计要装更多元素的时候,就可以调用它。如果新的容量new_capacity比当前容量大,它就会分配一块新的更大的内存空间new_data,然后把原来的元素都复制到新空间里。最后,释放原来的内存,把data指针指向新的内存空间,并且更新容量capacity_,这样 vector 就有了更大的空间来装元素啦😎。

(七)调整大小函数📏

template<typename T>
void MyVector<T>::resize(size_t new_size, T value) {
    if (new_size > size_) {
        if (new_size > capacity_) {
            reserve(new_size);
        }
        for (size_t i = size_; i < new_size; ++i) {
            data[i] = value;
        }
    } else if (new_size < size_) {
        for (size_t i = new_size; i < size_; ++i) {
            data[i].~T();  // 手动调用析构函数
        }
    }
    size_ = new_size;
}

😉这个函数就像是重新调整魔法口袋的大小和里面的物品数量一样🧐。如果新的大小new_size比当前元素数量size_大,它会先检查容量是否足够,如果不够就调用reserve函数扩容。然后,用指定的值value填充新增加的位置。如果新的大小比当前数量小,它会手动调用析构函数来清理多余的元素,最后更新元素数量size_😎。

 

(八)下标操作符重载🎯

  1. 非 const 版本
template<typename T>
T& MyVector<T>::operator[](size_t index) {
    return data[index];
}

😃这个非 const 版本的下标操作符重载就像一把神奇的钥匙🔑,可以让我们通过索引直接访问和修改 vector 中的元素。它返回data[index]的引用,这样我们就可以像操作普通变量一样对元素进行读写操作啦😎。
2. const 版本

template<typename T>
const T& MyVector<T>::operator[](size_t index) const {
    return data[index];
}

👀const 版本的下标操作符重载就像是一把只能看不能改的钥匙🔐,当我们有一个 const 的 vector 对象时,通过这个操作符只能读取元素的值,不能修改,确保了 const 对象的安全性😉。

 

(九)赋值操作符重载✍️

template<typename T>
MyVector<T>& MyVector<T>::operator=(const MyVector<T>& other) {
    if (this!= &other) {
        if (data!= nullptr) {
            delete[] data;
        }
        size_ = other.size_;
        capacity_ = other.capacity_;
        data = new T[capacity_];
        for (size_t i = 0; i < size_; ++i) {
            data[i] = other.data[i];
        }
    }
    return *this;
}

😎这个赋值操作符重载就像是把一个魔法口袋里的东西全部复制到另一个魔法口袋里一样🧙‍♂️。首先,它会检查是不是自我赋值,如果不是,就先释放掉原来的内存。然后,复制other的大小、容量和元素到当前 vector 中,最后返回当前对象的引用,这样就可以连续赋值啦😉。

(十)添加和删除元素函数🎯

  1. 在末尾添加元素函数
template<typename T>
void MyVector<T>::push_back(const T& value) {
    if (size_ == capacity_) {
        reserve(capacity_ == 0? 1 : capacity_ * 2);
    }
    data[size_++] = value;
}

😃这个函数就像是往魔法口袋的末尾放一个新东西一样🎁。它先检查口袋是否还有空间,如果没有了,就调用reserve函数扩容(如果当前容量是 0,就扩为 1,否则扩为原来的两倍)。然后,把新元素放到data[size_]的位置,并且把元素数量size_加 1😎。
2. 删除末尾元素函数

template<typename T>
void MyVector<T>::pop_back() {
    if (size_ > 0) {
        --size_;
        data[size_].~T();  // 手动调用析构函数
    }
}

👀这个函数就像是从魔法口袋的末尾拿出一个东西一样😉。如果 vector 不为空,它就把元素数量size_减 1,并且手动调用析构函数来清理最后一个元素,就像把拿出来的东西处理掉一样😎。
3. 在指定位置插入元素函数

template<typename T>
void MyVector<T>::insert(size_t pos, const T& value) {
    if (pos > size_) {
        return;
    }
    if (size_ == capacity_) {
        reserve(capacity_ == 0? 1 : capacity_ * 2);
    }
    for (size_t i = size_; i > pos; --i) {
        data[i] = data[i - 1];
    }
    data[pos] = value;
    ++size_;
}

😎这个函数就像是在排队的人群中插入一个小伙伴一样👫。它先检查插入位置pos是否合法,如果合法,就检查容量是否足够,不够就扩容。然后,把pos及后面的元素都往后移一位,腾出空间,把新元素插入到pos位置,最后把元素数量size_加 1😉。
4. 删除指定位置元素函数

template<typename T>
void MyVector<T>::erase(size_t pos) {
    if (pos >= size_) {
        return;
    }
    for (size_t i = pos; i < size_ - 1; ++i) {
        data[i] = data[i + 1];
    }
    --size_;
    data[size_].~T();  // 手动调用析构函数
}

👀这个函数就像是从排队的人群中请走一个小伙伴一样😉。它先检查删除位置pos是否合法,如果合法,就把pos后面的元素都往前移一位,覆盖掉要删除的元素,然后把元素数量size_减 1,并且手动调用析构函数来清理最后一个元素😎。

(十一)迭代器相关函数🔍

1.迭代器类型定义

template<typename T>
typedef T* MyVector<T>::iterator;

😃这里我们定义了迭代器的类型,其实就是一个指向T类型的指针,这样我们就可以用这个指针来遍历 vector 中的元素啦😉。
2. 起始迭代器函数

template<typename T>
typename MyVector<T>::iterator MyVector<T>::begin() {
    return data;
}

👀这个函数就像是给我们一个指向 vector 开头的小箭头一样,它返回data指针,也就是指向第一个元素的位置,让我们可以从这里开始遍历元素😎。
3. 结束迭代器函数

template<typename T>
typename MyVector<T>::iterator MyVector<T>::end() {
    return data + size_;
}

😉这个函数就像是给我们一个指向 vector 末尾后面一个位置的小箭头,它返回data + size_,表示遍历到这个位置就结束了,因为这个位置并不存储实际元素,只是一个结束的标志😎。


四、总结😎

通过模拟实现 vector 类,我们仿佛走进了动态数组的魔法世界🧙‍♂️,深入了解了其背后的实现原理和技术细节。从存储结构的精心设计,到各种操作函数的巧妙实现,再到内存管理的严谨把控,每一个环节都像是魔法世界里的一块拼图🧩,缺一不可。

希望大家继续保持探索的热情,不断挖掘编程世界里的更多奥秘😉!加油哦💪!


以后我将深入研究继承、多态、模板等特性,并将默认成员函数与这些特性结合,以解决更复杂编程问题!欢迎关注我👉【A Charmer】 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【ubuntu18.04】vm虚拟机复制粘贴键不能用-最后无奈换版本

我是ubuntu16版本的 之前费老大劲安装的vmware tools结果不能用 我又卸载掉&#xff0c;安装了open-vm-tools 首先删除VMware tools sudo vmware-uninstall-tools.pl sudo rm -rf /usr/lib/vmware-tools sudo apt-get autoremove open-vm-tools --purge再下载open-vm-tools s…

使用原生 OpenTelemetry 解锁各种可能性:优先考虑可靠性,而不是专有限制

作者&#xff1a;来自 Elastic Bahubali Shetti•Miguel Luna Elastic 现在支持使用 OTel Operator 在 Kubernetes 上部署和管理 Elastic Distributions of OpenTelemetry (EDOT)。SRE 现在可以访问开箱即用的配置和仪表板&#xff0c;这些配置和仪表板旨在通过 Elastic Observ…

【freertos】FreeRTOS信号量的介绍及使用

FreeRTOS信号量 一、概述二、PV原语三、函数接口1.创建一个计数信号量2.删除一个信号量3.信号量释放4.在中断释放信号量5.获取一个信号量&#xff0c;可以是二值信号量、计数信号量、互斥量。6.在中断获取一个信号量&#xff0c;可以是二值信号量、计数信号量7.创建一个二值信号…

【生物服务器】数据分析//论文润色/组学技术服务 、表观组分析、互作组分析、遗传转化实验、生物医学

DNA亲和纯化测序&#xff08;DAP-seq&#xff09;和组蛋白甲基化修饰是表观遗传学研究中两个重要的技术手段&#xff0c;它们在揭示基因表达调控机制和染色质结构动态变化中发挥着关键作用。然而&#xff0c;在实践过程中&#xff0c;这两种技术也存在一些痛点和挑战。 DNA亲和…

丹摩征文活动| 摩智云端深度解析:Faster R-CNN模型的训练与测试实战指南

目录 丹摩简介 文章前言Faster R-CNN的简介Faster RCNN的训练与测试提前准备1.1 mobaxterm&#xff08;远程连接服务器&#xff09;1.2 本文的源码下载 目标检测模型 Faster-Rcnn2.1云服务器平台 数据上传内置JupyterLab的使用本地连接使用DAMODEL实例获取实例的SSH访问信息通过…

二叉搜索树介绍

⼆叉搜索树 二叉搜索树的概念二叉搜索树的性能分析查找性能插入性能删除性能 二叉搜索树的插入二叉搜索树的查找二叉搜索树的删除⼆叉搜索树的实现代码测试代码 二叉搜索树key和key/value使⽤场景key搜索场景key/value搜索场景key/value⼆叉搜索树代码实现测试代码 二叉搜索树的…

7.揭秘C语言输入输出内幕:printf与scanf的深度剖析

揭秘C语言输入输出内幕&#xff1a;printf与scanf的深度剖析 C语言往期系列文章目录 往期回顾&#xff1a; VS 2022 社区版C语言的安装教程&#xff0c;不要再卡在下载0B/s啦C语言入门&#xff1a;解锁基础概念&#xff0c;动手实现首个C程序C语言概念之旅&#xff1a;解锁关…

5.4.2-1 编写Java程序在HDFS上创建文件

本次实战涉及使用Java操作Hadoop HDFS&#xff0c;包括创建文件、判断文件存在性及异常处理。通过手动添加依赖、启动HDFS服务&#xff0c;成功在HDFS上创建和检查文件。进一步探索了文件操作的最佳实践&#xff0c;如检查文件存在性以避免重复创建&#xff0c;以及处理HDFS安全…

RabbitMQ教程:路由(Routing)(四)

文章目录 RabbitMQ教程&#xff1a;路由&#xff08;Routing&#xff09;&#xff08;四&#xff09;一、引言二、基本概念2.1 路由与绑定2.2 Direct交换机2.3 多绑定2.4 发送日志2.5 订阅 三、整合代码3.1 EmitLogDirectApp.cs3.2 ReceiveLogsDirectApp.cs3.3 推送所有和接收e…

智云-一个抓取web流量的轻量级蜜罐v1.5

智云-一个抓取web流量的轻量级蜜罐v1.5 github地址 https://github.com/xiaoxiaoranxxx/POT-ZHIYUN 新增功能-自定义漏洞信息 可通过正则来添加相关路由以及响应来伪造 nacos的版本响应如下 日流量态势 月流量态势 抓取流量效果

21.UE5游戏存档,读档,函数库

2-23 游戏存档、读档、函数库_哔哩哔哩_bilibili 目录 1.存档蓝图 2.函数库 2.1保存存档 2.2读取存档&#xff1a; 3.加载游戏&#xff0c;保存游戏 3.1游戏实例对象 3.2 加载游戏 3.3保存游戏 这一节的内容较为错综复杂&#xff0c;中间没有运行程序进行阶段性成果的验…

实验5:网络设备发现、管理和维护

实验5&#xff1a;网络设备发现、管理和维护 实验目的及要求&#xff1a; 通过实验&#xff0c;掌握Cisco 路由器和交换机的IOS配置管理。自动从NTP服务器获取时间信息。能够利用TFTP服务器实现路由器和交换机配置文件的备份和恢复。同时验证CDP协议和LLDP协议的网络参数。完…

vue 项目使用 nginx 部署

前言 记录下使用element-admin-template 改造项目踩过的坑及打包部署过程 一、根据权限增加动态路由不生效 原因是Sidebar中路由取的 this.$router.options.routes,需要在计算路由 permission.js 增加如下代码 // generate accessible routes map based on roles const acce…

DataWorks on EMR StarRocks,打造标准湖仓新范式

在大数据领域&#xff0c;数据仓库和实时分析系统扮演着至关重要的角色。DataWorks 基于大数据引擎&#xff0c;为数据仓库/数据湖/湖仓一体等解决方案提供统一的全链路大数据开发治理平台&#xff0c;为用户带来智能化的数据开发和分析体验。而阿里云提供的 EMR Serverless St…

七、利用CSS和多媒体美化页面的习题

题目一&#xff1a; 利用CSS技术&#xff0c;结合表格和列表&#xff0c;制作并美化 “ 翡翠阁 ”页面。运行效果如下 运行效果&#xff1a; 代码 <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>翡翠阁</title>&…

游戏引擎学习第15天

视频参考:https://www.bilibili.com/video/BV1mbUBY7E24 关于游戏中文件输入输出&#xff08;IO&#xff09;操作的讨论。主要分为两类&#xff1a; 只读资产的加载 这部分主要涉及游戏中用于展示和运行的只读资源&#xff0c;例如音乐、音效、美术资源&#xff08;如 3D 模型和…

【动手学深度学习Pytorch】2. Softmax回归代码

零实现 导入所需要的包&#xff1a; import torch from IPython import display from d2l import torch as d2l定义数据集参数、模型参数&#xff1a; batch_size 256 # 每次随机读取256张图片 train_iter, test_iter d2l.load_data_fashion_mnist(batch_size) # 将展平每个…

51单片机基础05 实时时钟-思路及代码参考2、3

目录 一、思路二 1、原理图 2、代码 二、思路三 1、原理图 2、代码 一、思路二 所有设定功能相关的操作均在矩阵键盘进行实现&#xff0c;并在定时器中扫描、计数等 1、原理图 2、代码 #include <AT89X52.h> //调用51单片机的头文件 //------------------…

Notepad++的完美替代

由于Notepad的作者曾发表过可能在开发者代码中植入恶意软件的言论&#xff0c;他备受指责。在此&#xff0c;我向大家推荐一个Notepad的完美替代品——NotepadNext和Notepad--。 1、NotepadNext NotepadNext的特点&#xff1a; 1、跨平台兼容性 NotepadNext基于Electron或Qt…

Python | Leetcode Python题解之第564题数组嵌套

题目&#xff1a; 题解&#xff1a; class Solution:def arrayNesting(self, nums: List[int]) -> int:ans, n 0, len(nums)for i in range(n):cnt 0while nums[i] < n:num nums[i]nums[i] ni numcnt 1ans max(ans, cnt)return ans