【C++模拟实现】vector的模拟实现

news2025/1/11 15:05:35

【C++模拟实现】vector的模拟实现

目录

  • 【C++模拟实现】vector的模拟实现
      • vector模拟实现的标准代码
      • vector模拟实现中的要点
        • insert和erase会涉及到迭代器失效的问题
        • vector深度剖析
        • 关于模版template< class InputIterator >
        • 使用memcpy拷贝问题


作者:爱写代码的刚子

时间:2023.9.1

前言:模拟实现系列好久没更了,前来补更。

vector模拟实现的标准代码

namespace test
{
    template<class T>
    class vector
    {
    public:
        typedef T* iterator;
        typedef const T* const_iterator;
        iterator begin()
        {
            return _start;
        }
        iterator end()
        {
            return _finish;
        }
        const_iterator cbegin()
        {
            return _start;
        }
        const_iterator cend() const
        {
            return _finish;
        }

        vector()
        :_start(nullptr)
        ,_finish(nullptr)
        ,_endOfStorage(nullptr)
        {}
        vector(int n, const T& value = T())
        {
            resize(n,value);
        }
        template<class InputIterator>
        vector(InputIterator first, InputIterator last)
        :_start(nullptr)
        ,_finish(nullptr)
        ,_endOfStorage(nullptr)
        {
            //存疑,是否要reserve
            while(
                    first!=last)
            {
                push_back(*first);
                ++first;
            }
        }
        vector(const vector<T>& v)//这里是引用
        :_start(nullptr)
        ,_finish(nullptr)
        ,_endOfStorage(nullptr)
        {
            reserve(v.capacity());
            for(auto e:v)
            {
                push_back(e);
            }
        }
        vector<T>& operator= (vector<T> v)
        {
            swap(v);
            return *this;
        }
        ~vector()
        {
            delete[] _start;
            _start = nullptr;
            _finish = nullptr;
            _endOfStorage = nullptr;
        }

        size_t size() const
        {
            return _finish - _start;
        }
        size_t capacity() const
        {
            return _endOfStorage - _start;
        }
        void reserve(size_t n)
        {
            if(n>capacity()) {
                iterator tmp = new T[n];
                //拷贝数据(手动)
                int sz = size();
                if(_start) {
                    for (int i = 0; i < sz; i++) {
                        tmp[i] = _start[i];
                    }
                    delete[] _start;//一定要记得
                }

                _start = tmp;
                _finish = _start + sz;
                _endOfStorage = _start + n;
            }

        }
        void resize(size_t n, const T& value = T())
        {
            if(n<capacity())
            {
                _finish=_start + n;
            }
            else
            {
                reserve(n);
                while(_finish!=_start+n)
                {
                    *_finish=value;
                    ++_finish;
                }

            }
        }


        T& operator[](size_t pos)
        {
            assert(pos<size());
            return _start[pos];
        }
        const T& operator[](size_t pos)const
        {
            assert(pos<size());
            return _start[pos];
        }


        void push_back(const T& x)
        {
            insert(end(),x);

        }
        void pop_back()
        {
            erase(--end());//注意是--end(),end()指向的是数据的下一个位置
        }
        void swap(vector<T>& v)
        {
            std::swap(v._start,_start);
            std::swap(v._finish,_finish);
            std::swap(v._endOfStorage,_endOfStorage);

        }
        iterator insert(iterator pos, const T& x)
        {
            assert(pos>=_start&&pos<=_finish);
            if(_finish==_endOfStorage)
            {
                size_t len = pos - _start;
                size_t newcapacity = capacity()==0?4:capacity()*2;
                reserve(newcapacity);
                pos = _start + len;
            }
            iterator end = _finish - 1;
            while(end>=pos)
            {
                *(end+1) = *end;
                --end;//--end不是++end()
            }
            *pos = x;
            ++_finish;
            return pos;
        }
        iterator erase(iterator pos)
        {
            assert(pos>=_start&&pos<=_finish);
            iterator it=pos+1;
            while(it!=_finish)
            {
                *(it-1)=*it;
                ++it;
            }
            --_finish;
            return pos;

        }
    private:
        iterator _start; // 指向数据块的开始
        iterator _finish; // 指向有效数据的尾
        iterator _endOfStorage; // 指向存储容量的尾
    };
}

vector模拟实现中的要点

insert和erase会涉及到迭代器失效的问题

迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了 封装,比如:vector的迭代器就是原生态指针T*。因此迭代器失效,实际就是迭代器底层对应指针所指向的 空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器,程序可能会崩溃)。

有关迭代器失效的几种做法

  • 对于vector可能会导致其迭代器失效的操作有:

    1. 会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、 push_back等。

      出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释放掉, 而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的空间,而引起代码运行时崩溃。

      解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给it重新赋值即可。

    2. 指定位置元素的删除操作–erase

      erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。(但Linux下g++可能不一样,因为迭代器使用了原生指针,没有像vs一样进行封装)

**迭代器失效解决办法:在使用前,对迭代器重新赋值即可。 **

涉及到底层空间改变时,需要考虑重置迭代器

vector深度剖析

在这里插入图片描述

关于模版template< class InputIterator >

为什么不使用自己之前定义的iterator?

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

使用memcpy拷贝问题

为什么在reserve接口中不能直接使用memcpy进行拷贝?

  1. memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中
  2. 如果拷贝的是自定义类型的元素,memcpy即高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,memcpy的拷贝实际是浅拷贝。

所以在模版模拟实现中慎用memcpy!!!

结论:如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。


【附】:范围for是C++11的

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

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

相关文章

ShardingSphere——弹性伸缩原理

摘要 支持自定义分片算法&#xff0c;减少数据伸缩及迁移时的业务影响&#xff0c;提供一站式的通用弹性伸缩解决方案&#xff0c;是 Apache ShardingSphere 弹性伸缩的主要设计目标。对于使用单数据库运行的系统来说&#xff0c;如何安全简单地将数据迁移至水平分片的数据库上…

windows安装MongoDB后进入命令交互界面失败解决方案

MongoDB下载链接&#xff1a;https://www.mongodb.com/download-center MongoDB安装教程&#xff1a;https://juejin.cn/post/6844903912000978952 不要安装最新的高版本MongoDB&#xff0c;因为配置方法可能会有差别&#xff0c;比如7.0一直失败的话就换5.0及以下版本试试&am…

RDMA性能优化经验浅谈

一、RDMA概述 首先我们介绍一下RDMA的一些核心概念&#xff0c;当然了&#xff0c;我并不打算写他的API以及调用方式&#xff0c;我们更多关注这些基础概念背后的硬件执行方式和原理&#xff0c;对于这些原理的理解是能够写出高性能RDMA程序的关键。 Memory Region RDMA的网…

Vue:关于声明式导航中的 跳转、高亮、以及两个类名的定制

声明式导航-导航链接 文章目录 声明式导航-导航链接router-link的两大特点&#xff08;能跳转、能高亮&#xff09;声明式导航-两个类名定制两个高亮类名 实现导航高亮&#xff0c;实现方式其实&#xff0c;css&#xff0c;JavaScript , Vue ,都可以实现。其实关于路由导航&…

CF Edu152 C

Problem - C - Codeforces 题意&#xff1a; 思路&#xff1a; 首先&#xff0c;观察样例可知 这种是等效的 推广一下 0000.....111111 ..l..............r...... 这种是等效的 容易想到维护后面第一个1的位置和前面第一个0的位置&#xff0c;然后把所有区间都等效一下&…

Spring Aop--通知注解

一、环绕注解 环绕注解 环绕注解Aroud 注解描述AroundAround是Spring AOP中的一种通知类型&#xff0c;用于在目标方法执行前后进行环绕操作。它可以在方法调用前后增加额外的逻辑&#xff0c;例如日志记录、性能监控等。Around注解需要配合AspectJ表达式来指定切入点&#…

el-date-picker自定义只能选中当前月份和半年内月份等

需求&#xff1a;el-date-picker只能选中当前月期和当前月期往前半年&#xff0c;其他时间就禁用了不让选择了&#xff0c;因为没数据哈哈。当然也可以选择往前一年等。 一、效果 二、写个日期选择器 :picker-options&#xff1a;日期选项 value-format&#xff1a;选择后的格…

Revit SDK 介绍:AvoidObstruction 避免碰撞

前言 这个例子介绍如何让碰撞在一起的管道避免碰撞&#xff0c;即对管道进行调整。 内容 调整前&#xff1a; 调整后&#xff1a; 从结果来看&#xff0c;所有的碰撞都被调整了。作为一个例子&#xff0c;不会去考虑是否合理&#xff0c;仅仅是展示了一下 Revit API 的能…

51单片机智能电风扇控制系统proteus仿真设计( 仿真+程序+原理图+报告+讲解视频)

51单片机智能电风扇控制系统仿真设计( proteus仿真程序原理图报告讲解视频&#xff09; 讲解视频1.主要功能&#xff1a;2.仿真3. 原理图4. 程序代码5.设计报告6. 设计资料内容清单 51单片机智能电风扇控制系统仿真设计( proteus仿真程序原理图报告讲解视频&#xff09; 仿真图…

日志记录一

我们知道&#xff0c;当注释掉配置环境变量 export ……之后&#xff0c;需要source ……之后&#xff0c;修改内容才能生效&#xff1b;例如&#xff1a;将export的Java环境变量注释掉或者删除掉后&#xff0c; vi /etc/profile source /etc/profile 使之生效。 但是&#xf…

专访张少光---国内著名牛散、实战专家

导读&#xff1a;新财富最佳分析师评选作为中国本土第一份市场化的分析师评选&#xff0c;自2003年开启至今已20年&#xff0c;通过公正、公平、公开的评选&#xff0c;与市场各方共同挖掘了大量优秀分析师。值此新财富最佳分析师评选20周年之际&#xff0c;我们期望通过《对话…

CCF-CSP 30次 第二题【矩阵运算】

计算机软件能力认证考试系统 #include<bits/stdc.h> using namespace std; const int N1e410; #define int long long int n,d; int q[N][22],k[22][N],v[N][22],w[N]; int ans1[N][22],ans2[N][22]; signed main() {scanf("%lld %lld",&n,&d);for(in…

nvm安装后,安装并切换版本,node报错

1、下载 下载地址 https://github.com/coreybutler/nvm-windows/releases 这个版本是 v1.1.11&#xff0c;不喜欢的话&#xff0c;自己选版本 下载setup.exe&#xff0c;安装 2、安装 安装时候 安装路径可以自选&#xff0c;但是涉及到指向的路径&#xff0c;最好让它自己选&…

【实操干货】如何开始用Qt Widgets编程?(四)

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 在本文中&#xff0…

Self-supervised 3D Human Pose Estimation from a Single Image

基于单幅图像的自监督三维人体姿态估计 主页&#xff1a; https://josesosajs.github.io/ imagepose/ 源码&#xff1a;未开源 摘要 我们提出了一种新的自我监督的方法预测三维人体姿势从一个单一的图像。预测网络是从描绘处于典型姿势的人的未标记图像的数据集和一组未配对…

论文解读 | 三维点云深度学习的综述

原创 | 文 BFT机器人 KITTI 是作为基准测试是自动驾驶中最具影响力的数据集之一&#xff0c;在学术界和工业界都被广泛使用。现有的三维对象检测器存在着两个限制。第一是现有方法的远程检测能力相对较差。其次&#xff0c;如何充分利用图像中的纹理信息仍然是一个开放性的问题…

Qt应用开发(基础篇)——错误提示框 QErrorMessage

一、前言 QErrorMessage类继承于QDialog&#xff0c;是一个用来显示错误信息的对话框。 提示框QDialog 消息对话框 QMessageBox QErrorMessage错误消息对话框提供了一个主文本窗口、一个复选框、一个图标和按钮。文本框用来显示错误信息&#xff0c;复选框用来让用户选择未来是…

【实训项目】传道学习助手APP设计

1.设计摘要 跨入21世纪以来,伴随着时代的飞速发展&#xff0c;国民对教育的重视度也有了进一步的提升。我们不难发现虽然很多学习内容有学习资料或者答案&#xff0c;但是这些内容并不能达到让所有求学的人对所需知识进行完全地理解与掌握。所以我们需要进行提问与求助。那么一…

深入RocketMQ生产者负载均衡(顺序消息场景):剖析MessageGroupHash模式和SipHash算法

文章首发地址 RocketMQ生产者负载均衡中的MessageGroupHash模式 在RocketMQ顺序消息场景&#xff0c;默认使用MessageGroupHash模式的负载均衡策略。 MessageGroupHash模式下&#xff0c;生产者发送消息时&#xff0c;以消息组为粒度&#xff0c;按照内置的Hash算法&#xf…