vector模拟实现——关于模拟中的易错点

news2024/12/23 18:42:04

前言

vector 本质上类似数组,也可以理解为一种泛型的 string。string 只能存储 char 类型,但是 vector 支持各种内置类型和自定义类型。本次将围绕模拟实现 vector 中遇到的问题进行分析。

文章目录

  • 前言
  • 一、确定思路
  • 二、实现过程
    • 2.1 查阅文档
    • 2.2 验证正确性
  • 三、易错问题
    • 3.1 insert 导致迭代器失效
    • 3.2 erase 导致迭代器失效
    • 3.3 浅拷贝问题
  • 四、完整代码

一、确定思路

同 string 一样,在进行模拟之前我们要确定 vector 的结构是怎么样的,以及有哪些函数可用。

在这里插入图片描述

由图中我们可以知道 vector 所需要的函数相对于 string 较少,这主要是因为 vector 更支持泛型,因此类似于 string 的字典序比较等都不需要实现,毕竟不是每一个类型都是字典序比较。

下面给出一个 vector 的实现框架。

namespace hty{
	template<class T>
    class vector {
    private:
        typedef T* iterator;
        typedef const T* const_iterator;
        iterator _start; // 指向数据块的开始
        iterator _finish; // 指向有效数据的尾
        iterator _endOfStorage; // 指向存储容量的尾

    public:
        // iterator

        iterator begin() {
        }

        iterator end() {
        }

        const_iterator cbegin() const {
        }

        const_iterator cend() const {
        }
        
        
        // construct and destroy

        vector() {}
        
        vector(int n, const T& value = T()) {
        }
        
        vector(size_t n, const T& value = T()) {
        }

        template<class InputIterator>
        vector(InputIterator first, InputIterator last) {
        }

        vector(const vector<T>& v) {
        }

        vector<T>& operator= (vector<T> v) {
        }

        ~vector() {
        }
        

        // capacity

        size_t size() const {
        }

        size_t capacity() const {
        }

        bool empty() {
        }

        void clear() {
        }

        void reserve(size_t n) {
        }

        void resize(size_t n, const T& value = T()) {
        }
        
        
        //access

        T& operator[](size_t pos) {
        }
        
        const T& operator[](size_t pos)const {
        }
        
        
        // modify

        void push_back(const T& x) {
        }

        void pop_back() {
        }

        void swap(vector<T>& v) {
        }

        iterator insert(iterator pos, const T& x) {
        }

        iterator erase(iterator pos) {
        }
    };
}

二、实现过程

2.1 查阅文档

和模拟 string 时一样,模拟最重要的一步还是查阅文档,了解函数参数和功能。例如之前模拟实现 string 时,模拟 insert 函数传参是插入位置的下标,但是在 vector 中我们则传入的是迭代器。诸此之类的细节都需要在查阅文档后了解。

2.2 验证正确性

比起 string,vector 更需要对正确性的验证。虽然看似两者功能相近,但是由于 vector 更多时候面向的是自定义类型,因此更容易出现错误,而此类错误往往是连锁性的。

三、易错问题

在模拟实现过程中有两个常见的迭代器失效问题,这两处错误非常容易出现,分别由 insert 和 erase 引起。

3.1 insert 导致迭代器失效

insert 导致迭代器失效的原因在扩容。当我们对一个迭代器所指向位置插入数据时,其余数据向后移动。在原本剩余空间充足的情况下迭代器正常,但是如果剩余空间不够,需要扩容时,很多人容易忽略原 pos 迭代器会失效,具体失效原理如下:

在这里插入图片描述

3.2 erase 导致迭代器失效

erase 不存在扩容等改变原数组空间分配的情况,乍一看似乎不容易出现错误,但是在 string 中我们提到要注意特殊情况的正确性验证。如果我们删除的是最后一个位置的元素,那么之后 pos 迭代器指向的就是一块没有意义的地址。对于 vs 等迭代器非原生指针实现的情况,如果检查严格,就会中断报错。具体失效原理如下:

在这里插入图片描述

3.3 浅拷贝问题

不同于 string,vector 更多面向自定义类型。类似于重载 = 运算符,string 可以直接调用 memcpy 进行实现,但是对于 vector 则不行,vector 内容中也可能包含指针,需要自行开辟空间。最经典的例子就是 vector<vector>,因此在实现过程中,许多原本在 string 中 memcpy 可以解决的问题,我们需要用循环 + new 进行实现。这更体现了 C++ new 用于实现类型初始化的特性。

四、完整代码

namespace hty {
    template<class T>
    class vector {
    private:
        typedef T* iterator;
        typedef const T* const_iterator;
        iterator _start; // 指向数据块的开始
        iterator _finish; // 指向有效数据的尾
        iterator _endOfStorage; // 指向存储容量的尾

    public:
        // iterator

        iterator begin() {
            return _start;
        }

        iterator end() {
            return _finish;
        }

        const_iterator cbegin() const {
            return _start;
        }

        const_iterator cend() const {
            return _finish;
        }
        
        
        // construct and destroy

        vector() : _start(nullptr), _finish(nullptr), _endOfStorage(nullptr) {}
        
        vector(int n, const T& value = T()) : _start(nullptr), _finish(nullptr), _endOfStorage(nullptr) {
            resize(n, value);
        }
        
        vector(size_t n, const T& value = T()) : _start(nullptr), _finish(nullptr), _endOfStorage(nullptr) {
            resize(n, value);
        }

        template<class InputIterator>
        vector(InputIterator first, InputIterator last) : _start(nullptr), _finish(nullptr), _endOfStorage(nullptr) {
            while (first != last) {
                push_back(*first);
                first++;
            }
        }

        vector(const vector<T>& v) {
            vector<T> tmp(v.begin(), v.cend());
            swap(v);
        }

        vector<T>& operator= (vector<T> v) {
            swap(v);
            return *this;
        }

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

        // capacity

        size_t size() const {
            return _finish - _start;
        }

        size_t capacity() const {
            return _endOfStorage - _start;
        }

        bool empty() {
            return _start == _finish;
        }

        void clear() {
            _finish = _start;
        }

        void reserve(size_t n) {
            if (n > capacity()) {
                size_t old_size = size();
                T* tmp = new T[n];
                if (_start) {
                    for (int i = 0; i < old_size; i++)
                        tmp[i] = _start[i];
                    delete[] _start;
                }
                _start = tmp;
                _finish = _start + old_size;
                _endOfStorage = _start + n;
            }
        }

        void resize(size_t n, const T& value = T()) {
            if (n > capacity())
                reserve(n);
            if (n > size()) {
                for (int i = size(); i < n; i++)
                    _start[i] = value;
            }
            _finish = _start + n;
        }



        
        
        //access

        T& operator[](size_t pos) {
            return _start + pos;
        }
        
        const T& operator[](size_t pos)const {
            return _start + pos;
        }
        
        
        // modify

        void push_back(const T& x) {
            if (size() == capacity()) {
                size_t new_capacity = capacity() == 0 ? 4 : 2 * capacity();
                reserve(new_capacity);
            }
            *_finish = x;
            _finish++;
        }

        void pop_back() {
            assert(_finish > _start);
            --_finish;
        }

        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 >= _start && pos <= _finish);
            if (size() == capacity()) {
                size_t len = pos - _start;
                size_t new_capacity = capacity() == 0 ? 4 : 2 * capacity();
                reserve(new_capacity);
                pos = _star + len;
            }
            for (auto i = _finish; i > pos; i--)
                *i = *(i - 1);
            *pos = x;
            _finish++;
            return pos;
        }

        iterator erase(iterator pos) {
            assert(pos >= _start && pos < _finish);
            for (auto x = pos; x < _finish - 1; x++)
                *x = *(x + 1);
            --_finish;
            return pos;
        }
    };
}

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

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

相关文章

4-3 nn.functional和nn.Module

一&#xff0c;nn.functional 和 nn.Module 前面我们介绍了Pytorch的张量的结构操作和数学运算中的一些常用API。利用这些张量的API我们可以构建出神经网络相关的组件(如激活函数&#xff0c;模型层&#xff0c;损失函数)。 其实&#xff1a;Pytorch和神经网络相关的功能组件大…

中小企业数字化转型难?为什么不试试“企业级”无代码平台

首先&#xff0c;让我们思考一下&#xff0c;中小企业为什么要进行数字化转型&#xff1f;随着全球经济的数字化趋势日益明显&#xff0c;中小企业作为经济的重要组成部分&#xff0c;其数字化转型已成为推动经济高质量发展的关键。数字技术可以帮助中小企业提高生产效率、降低…

ctfshow-web-红包题 辟邪剑谱

0x00 前言 CTF 加解密合集CTF Web合集网络安全知识库溯源相关 文中工具皆可关注 皓月当空w 公众号 发送关键字 工具 获取 0x01 题目 0x02 Write Up 这道题主要是考察mysql查询绕过的问题。 首先访问后看到是一个登录页面&#xff0c;测试注册等无果 扫描目录&#xff0c;发…

Packet Tracer的使用介绍

直接访问 Packet Tracer 的帮助页面、教程视频和在线资源对于了解该软件会更加方便。 单击菜单工具栏右上角的问号图标。单击“帮助”菜单&#xff0c;然后选择“内容”。 b. 通过单击“帮助”>“教程”来访问 Packet Tracer 的教程视频。 菜单栏&#xff1a;提供文件、编辑…

SpringBoot运行原理

目录 SpringBootApplication ComponentScan SpringBootConfiguration EnableAutoConfiguration 结论 SpringbootApplication&#xff08;主入口&#xff09; SpringBootApplication public class SpringbootConfigApplication {public static void main(String[] args) {…

Android动态片段

之前创建的片段都是静态的。一旦显示片段&#xff0c;片段的内容就不能改变了。尽管可以用一个新实例完全取代所显示的片段&#xff0c;但是并不能更新片段本身的内容。 之前已经创建过一个基础秒表应用&#xff0c;具体代码https://github.com/MADMAX110/Stopwatch。我们将这个…

发生以下的报错怎么办?

报错问题&#xff1a; 解决办法&#xff1a; 根据你提供的代码和错误信息&#xff0c;问题出在使用了nullptr。这个错误是因为你的编译器不支持C11标准。 nullptr是C11引入的空指针常量。为了解决这个问题&#xff0c;你可以尝试以下两种方法之一&#xff1a; 1. 将nullptr…

想要精通算法和SQL的成长之路 - 可以攻击国王的皇后

想要精通算法和SQL的成长之路 - 可以攻击国王的皇后 前言一. 可以攻击国王的皇后 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 可以攻击国王的皇后 原题链接 这个题目其实并没有涉及到什么很难的算法&#xff0c;其实就是一个简单的遍历题目。核心思想&#xff1a; 以…

CRM系统销售自动化功能如何提高销售效率

销售效率对企业的盈利能力有着至关重要的联系。提高销售效率&#xff0c;就是要提高销售人员的工作效率和销售转化率。那么&#xff0c;企业如何提高销售效率呢&#xff1f;CRM销售自动化功能可以帮助企业实现这一目标。 一、线索管理 线索是指有潜在购买意向的客户&#xff…

kali必杀器之三剑客

Kali常见攻击手段 注意:仅用于教程和科普&#xff0c;切勿做违法之事&#xff0c;否则后果自负 1 网络攻击手段 请正确使用DDos和CC攻击&#xff0c;不要用来做违反当地法律法规的事情&#xff0c;否则后果自负 使用之前kali需要能够上网 参考:kali安装 1.1 DDos攻击…

新加坡打车软件平台Ryde Group申请1700万美元纳斯达克IPO上市

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;新加坡打车软件平台Ryde Group近期已向美国证券交易委员会&#xff08;SEC&#xff09;提交招股书&#xff0c;申请在纳斯达克IPO上市&#xff0c;股票代码为&#xff08;RYDE&#xff09;&#x…

学习javaEE初阶的第一堂课

学习金字塔 java发展简史 Java最初诞生的时候是用来写前端的!! 199x年 199x年,互联网还处在比较早期的阶段,当时主流的编程语言是 C/C, 有个大佬要搞个"智能面包机",觉得用C来做太难了 于是就基于C搞了个简单点的语言,Java 就诞生了~~ 遗憾的是项目流产了,没做成…

【SpringMVC】自定义注解

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Spring MVC》。&#x1f3…

MMrotate_dev 1.x训练自己的数据集

因为MMRotate dev 1.x 新增了PSC角度编码器以及RTMDet目标检测算法&#xff0c;而之前从官网下载的MMRotate是main分支&#xff0c;没有新增的东西&#xff0c;所以重新搞了一下&#xff0c;以此记录。 环境配置 1.创建虚拟环境 注意&#xff1a;如果之前安装了MMRotate的其…

基于小程序的理发店预约系统

一、项目背景及简介 现在很多的地方都在使用计算机开发的各种管理系统来提高工作的效率&#xff0c;给人们带来很多的方便。计算机技术从很大的程度上解放了人们的双手&#xff0c;并扩大了人们的活动范围&#xff0c;是人们足不出户就可以通过电脑进行各种事情的管理。信息系…

pycharm安装jupyter,用德古拉主题,但是输入行全白了,看不清,怎么办?

问题描述 今天换了以下pycharm主题&#xff0c;但是jupyter界面输入代码行太白了&#xff0c;白到看不清楚这行的字&#xff0c;更不知道写的是什么&#xff0c;写到哪了&#xff0c;这还是挺烦人的&#xff0c;其他都挺正常的。 问题分析 目前来看有两个原因&#xff1a; 1、…

深化产教融合,知了汇智助力高校数字化人才培养

随着数字经济的不断深入和发展&#xff0c;数字人才短缺的问题逐渐凸显&#xff0c;根据相关报告&#xff0c;目前我国数字人才缺口在2500万到3000万左右&#xff0c;且缺口仍在不断扩大。为了满足数字经济的发展需求&#xff0c;如何培养出具备创新型、复合型、应用型能力的数…

C++学习笔记一(重载、类)

C 1、函数重载2、类2.1、类的方法和属性2.2、类的方法的定义2.3、构造器和析构器2.4、类的实例化2.5、基类与子类2.6、类的public、protected、private继承2.7、类的方法的重载2.8、子类方法的覆盖2.9、继承中的构造函数和析构函数 1、函数重载 函数重载大概可以理解为&#x…

再次理解Android账号管理体系

目录 ✅ 0. 需求 &#x1f4c2; 1. 前言 &#x1f531; 2. 使用 2.1 账户体系前提 2.2 创建账户服务 2.3 操作账户-增删改查 &#x1f4a0; 3. 源码流程 ✅ 0. 需求 试想&#xff0c;自己去实现一个账号管理体系&#xff0c;该如何做呢&#xff1f; ——————————…

竞赛 基于大数据的时间序列股价预测分析与可视化 - lstm

文章目录 1 前言2 时间序列的由来2.1 四种模型的名称&#xff1a; 3 数据预览4 理论公式4.1 协方差4.2 相关系数4.3 scikit-learn计算相关性 5 金融数据的时序分析5.1 数据概况5.2 序列变化情况计算 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &…