C++vector类的模拟实现

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

模拟实现vector类

收录于专栏【C++语法基础
本专栏旨在分享学习C++的一点学习笔记,欢迎大家在评论区交流讨论💌

 

目录

前置说明

1. vector的迭代器

2. vector的构造和析构

2.1 构造函数:

2.2 赋值操作符重载

2. 3 析构函数 

3. vector容量的操作

3.1 size

3.2 capcity

3.3 reserve

3.4 resize

4. vector的修改

4.1 push_back

4.2 pop_back

4.3 swap

4.4 insert

4.5 erase

5. vector对象的访问

6. 测试模拟实现的vector

6.1 测试vector的构造

6.2 测试vector的容量操作

6.3 测试vector的修改

6.4 测试vector对象的访问 


前置说明

这里需要模拟实现vector类的操作有:

namespace my_vector
{template<class T>class vector{public:// Vector的迭代器是一个原生指针typedef T* iterator;typedef const T* const_iterator;iterator begin();iterator end();const_iterator cbegin();const_iterator cend() const;// construct and destroyvector();vector(int 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();// capacitysize_t size() const ;size_t capacity() const;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);private:iterator _start; // 指向数据块的开始iterator _finish; // 指向有效数据的尾iterator _endOfStorage; // 指向存储容量的尾};
}

1. vector的迭代器

  // Vector的迭代器是一个原生指针typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator cbegin()const{return _start;}const_iterator cend() const{return _finish;}

iterator 和 const_iterator 分别是指向元素的普通指针和常量指针begin() 和 end() 方法返回容器的起始和结束迭代器,允许遍历元素。而 cbegin() 和 cend() 方法提供了常量迭代器,适用于只读访问。整体上,这些方法提供了对容器元素的访问方式,便于使用标准迭代器模式进行遍历。 

2. vector的构造和析构

2.1 构造函数:

vector() : _start(nullptr), _finish(nullptr), _endOfStorage(nullptr)
{}vector(int n, const T& value = T()): _start(nullptr), _finish(nullptr), _endOfStorage(nullptr)
{reserve(n);while (n--){push_back(value);}
}template<class InputIterator>
vector(InputIterator first, InputIterator last)
{reserve(last - first);while (first != last){push_back(*first);++first;}
}vector(const vector<T>& v): _start(nullptr), _finish(nullptr), _endOfStorage(nullptr)
{reserve(v.capacity());iterator it = begin();const_iterator vit = v.cbegin();while (vit != v.cend()){*it++ = *vit++;}_finish = _start + v.size();_endOfStorage = _start + v.capacity();}

1. 默认构造函数 vector() :

        初始化三个指针 _start, _finish, 和 _endOfStorage 为 nullptr,表示一个空的容器。


2. 带大小和初始值的构造函数 vector(int n, const T& value = T()) :

            使用 reserve(n) 预留存储空间,以便存放 n 个元素。
            通过 push_back(value) 将 value 添加到容器中,重复 n 次以填充容器。


3. 范围构造函数 template<class InputIterator> vector(InputIterator first, InputIterator last) :

            计算输入迭代器之间的距离并调用 reserve。
            使用 push_back(*first) 逐个添加元素,直到 first 达到 last。


4. 拷贝构造函数 vector(const vector<T>& v) :

            先调用 reserve(v.capacity()) 为新容器分配与源容器相同的容量。
            使用迭代器逐个复制元素,从源容器的常量迭代器 vit 读取,并将其值赋给当前容器的迭代器 it。
            最后,更新 _finish 和 _endOfStorage 指针,确保它们正确指向新容器的结束和存储空间的边界。

 

2.2 赋值操作符重载

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

参数为值传递:

vector<T> v 会创建传入对象的一个副本。这一副本会通过拷贝构造函数生成,保证了原对象不受影响。

调用 swap(v):

swap 函数交换当前对象(*this) 和副本 v 的内容。这种做法能够高效地处理资源管理,避免多次内存分配和释放,减少了潜在的异常风险。

返回* this:

最后,返回对当前对象的引用,使得赋值操作可以链式调用,例如 a = b = c。

 

2. 3 析构函数 

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

1. 内存释放:delete[] _start;

这行代码释放了之前通过动态分配(通常是在 reserve 或其他构造函数中)分配的内存。由于 _start 指向的是一个动态数组,所以使用 delete[] 来确保正确地释放整个数组。
2. 指针重置:_start = _finish = _endOfStorage = nullptr;

这一行将三个指针重置为 nullptr,以防止悬挂指针。虽然析构函数会在对象销毁后自动调用,但将指针设为 nullptr 是一种良好的编程习惯,能够减少错误风险,尤其是在调试时。
3. 异常安全:

析构函数通常不应该抛出异常,因此在此处处理资源释放时采用了简单直接的方法,确保即使在异常情况下也能正常释放资源。

3. vector容量的操作

3.1 size

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

3.2 capcity

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

3.3 reserve

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

1. 容量检查:if(n > capacity())

        首先检查请求的容量 n 是否大于当前容量。如果不大于,则不需要重新分配内存,函数直接返回。
2. 保存旧大小:size_t oldSize = size();

        记录当前元素的数量,以便在新内存中复制元素。
3. 动态分配新内存:T* tmp = new T[n];

        分配一个新的数组 tmp,大小为 n。
4. 复制旧数据:

        如果 _start 指针不为 nullptr(表示当前有存储的元素),则使用循环将旧数组中的元素复制到新数组 tmp。
5. 更新指针:

        _start 被更新为指向新分配的数组 tmp。
        _finish 更新为 _start + oldSize,指向已复制的元素末尾。
        _endOfStorage 更新为 _start + n,表示新数组的容量边界。

 

3.4 resize

void resize(size_t n, const T& value = T())
{// 1.如果n小于当前的size,则数据个数缩小到nif (n <= size()){_finish = _start + n;return;}// 2.空间不够则增容if (n > capacity())reserve(n);// 3.将size扩大到niterator it = _finish;iterator _finish = _start + n;while (it != _finish){*it = value;++it;}
}

1. 缩小大小:如果新的大小小于或等于当前大小,直接将结束指针 _finish 移动到新大小,丢弃多余的元素。

2. 增容:如果新大小大于当前容量,调用 reserve 来确保 vector 有足够的空间。

3. 扩展大小:将结束指针更新到新大小,并用指定的值初始化新添加的元素,直到达到新的结束指针。

关键点总结:
当缩小时,只调整指针而不调用析构函数。
在增容时,通过 reserve 确保内存足够。
在扩展时,初始化新元素以保持一致性。 

4. vector的修改

4.1 push_back

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

4.2 pop_back

        void pop_back(){erase(--end());}

4.3 swap

        void swap(vector<T>& v){swap(_start, v._start);swap(_finish, v._finish);swap(_endOfStorage, v._endOfStorage);}

1. 指针交换:通过 swap 函数交换 _start、_finish 和 _endOfStorage 三个指针。这意味着两个 vector 将交换它们的内部存储,实际上并没有复制数据,而是简单地交换指针。

2. 高效性:这个交换操作非常高效,因为它只涉及指针的交换,而不需要移动任何元素或重新分配内存。

3. 异常安全:swap 的实现是标准库提供的版本(通常是 noexcept),那么这个函数也具有异常安全性。

4. 状态一致性:通过交换指针,两个 vector 的状态完全对调,原有的内存管理也随之转移,确保了资源的正确管理。

 

4.4 insert

        iterator insert(iterator pos, const T& x){assert(pos <= _finish);// 空间不够先进行增容if (_finish == _endOfStorage){size_t len = pos - _start;size_t newCapacity = (0 == capacity()) ? 1 : capacity() * 2;reserve(newCapacity);// 如果发生了增容,需要重置pospos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;}

1. 参数和前提条件
        参数:
                pos:插入位置的迭代器,指向希望插入新元素的地方。
                x:要插入的元素。
        前提条件:
                使用 assert 确保 pos 不超过 _finish,即插入位置在有效范围内。
2. 增容逻辑
        判断是否需要增容:
                检查 _finish 是否等于 _endOfStorage,即当前是否已满。
                如果已满,计算新的容量(当前容量的两倍,或初始为 1)。
                调用 reserve 函数来分配新的内存。
        调整插入位置:
                如果进行了增容,需要重新计算插入位置 pos,因为内存可能已改变,pos 应该重新定位。
3. 元素移动
        移动元素:
                从 _finish - 1 开始,向后移动元素,将每个元素向右移动一位,以腾出插入位置。
                通过循环将元素依次复制到它们的下一个位置,直到移动到 pos。
4. 插入元素
        在计算得出的 pos 位置插入新元素 x。
5. 更新结束指针
        增加 _finish 的值,表示 vector 的大小已增加。
6. 返回值
        返回插入位置的迭代器 pos,以便后续操作或链式调用。 

4.5 erase

 // 返回删除数据的下一个数据// 方便解决:一边遍历一边删除的迭代器失效问题iterator erase(iterator pos){// 挪动数据进行删除iterator begin = pos + 1;while (begin != _finish){*(begin - 1) = *begin;++begin;}--_finish;return pos;}

5. vector对象的访问

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

6. 测试模拟实现的vector

6.1 测试vector的构造

void Text_my_vector1()
{my_vector::vector<int> ret1;my_vector::vector<int> ret2(3, 666);my_vector::vector<int> ret3(ret2);my_vector::vector<int> ret4 = ret2;for (auto ch : ret1)cout << ch << endl;cout << endl;for (auto ch : ret2)cout << ch << endl;cout << endl;for (auto ch : ret3)cout << ch << endl;cout << endl;for (auto ch : ret4)cout << ch << endl;cout << endl;
}

 

6.2 测试vector的容量操作

void Text_my_vector2()
{my_vector::vector<int> ret(100, 999);cout << ret.size() << endl;cout << ret.capacity() << endl;cout << endl;ret.push_back(666);cout << ret.size() << endl;cout << ret.capacity() << endl;cout << endl;ret.reserve(10);cout << ret.capacity() << endl;cout << endl;ret.reserve(100);cout << ret.capacity() << endl;cout << endl;ret.reserve(1000);cout << ret.capacity() << endl;cout << endl;ret.resize(100, 1);cout << ret.size() << endl;cout << ret.capacity() << endl;cout << endl;ret.resize(10, 2);cout << ret.size() << endl;cout << ret.capacity() << endl;cout << endl;}

 

6.3 测试vector的修改

void Text_my_vector3()
{my_vector::vector<int> ret1(10, 1);my_vector::vector<int> ret2(6, 666);//迭代器遍历my_vector::vector<int>::iterator it = ret1.begin();while (it < ret1.end()){cout << *it << " ";it++;}cout << endl;//for范围遍历for (auto ch : ret2)cout << ch << " ";cout << endl;ret1.push_back(666);ret1.push_back(666);ret1.push_back(666);for (auto ch : ret1)cout << ch << " ";cout << endl;for (auto ch : ret2)cout << ch << " ";cout << endl;for (auto ch : ret1)cout << ch << " ";cout << endl;for (auto ch : ret2)cout << ch << " ";cout << endl;ret1.insert(ret1.begin() + 3, 888);ret2.insert(ret1.begin() + 3, 888);for (auto ch : ret1)cout << ch << " ";cout << endl;for (auto ch : ret2)cout << ch << " ";cout << endl;ret1.erase(ret1.end() - 9);ret2.erase(ret1.end() - 9);for (auto ch : ret1)cout << ch << " ";cout << endl;for (auto ch : ret2)cout << ch << " ";cout << endl;
}

 

6.4 测试vector对象的访问 

void Text_my_vector4()
{my_vector::vector<int> ret(10, 888);cout << ret[7] << endl;ret.insert(ret.begin() + 7, 666);cout << ret[7] << endl;
}

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

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

相关文章

数据结构2——单链表

目录 1.链表 1.1链表的概念及结构 1.2 链表的分类 ​编辑2.无头单链表的实现 1. 节点 2.遍历链表 3.动态增加新节点 4.查找&#xff08;修改&#xff09; 5.插入 5.1 尾插 5.2 头插 5.3 在pos之前插入x 5.4 在pos之后插入x 6.删除 6.1 尾删 6.2 头删 6.3 删除…

YOLOv10改进,YOLOv10损失函数更换为Powerful-IoU(2024年最新IOU),助力高效涨点

改进前训练结果: 改进后的结果: 摘要 边界框回归(BBR)是目标检测中的核心任务之一,BBR损失函数显著影响其性能。然而,观察到现有基于IoU的损失函数存在不合理的惩罚因子,导致回归过程中锚框扩展,并显著减缓收敛速度。为了解决这个问题,深入分析了锚框扩展的原因。针…

狂神说多线程01

线程实现&#xff08;重点&#xff09; 多线程三个方法 继承Thread类 ⭐️实现Runnable 实现callable&#xff08;了解&#xff09; 线程状态 出生-&#xff1f; 线程同步&#xff08;重点&#xff09; &#xff08;多个线程操作同一个对象&#xff0c;那个对象出现了不安…

RP2040 CXX SDK PIO应用例程

RP2040 CXX SDK PIO应用例程 &#x1f4cd;DS18B20 PIO参考项目例程&#xff1a;https://github.com/jondurrant/RP2040PIO-DS18B20&#x1f4cd;DHT11 PIO 参考项目例程&#xff1a;https://github.com/vmilea/pico_dht 在官方的SDK pico-examples中有关PIO的例程有20个&#…

828华为云征文 | 云服务器Flexus X实例,Docker集成搭建Halo博客平台

828华为云征文 | 云服务器Flexus X实例&#xff0c;Docker集成搭建Halo博客平台 Halo博客平台是一款基于Java的开源博客系统&#xff0c;以其简单易用、功能强大、美观大方等特点而受到广泛欢迎&#xff0c;采用了多种先进的技术框架&#xff0c;包括Freemarker模板引擎、Vue.j…

【STM32】【rt-thread】startup_stm32f405xx.S文件解读

startup_stm32f405xx.S文件解读 一、代码全文 /********************************************************************************* file startup_stm32f405xx.s* author MCD Application Team* brief STM32F405xx Devices vector table for GCC based toolcha…

每日OJ题_牛客_ 游游的you(贪心+模拟)

目录 牛客_ 游游的you&#xff08;贪心模拟&#xff09; 解析代码 牛客_ 游游的you&#xff08;贪心模拟&#xff09; 游游现在有a个y&#xff0c;b个o&#xff0c;c个u&#xff0c;他想用这些字母拼成一个字符串。 三个相邻的字母是"you"可以获得2分&#xff0c…

室内院内常见的不知名蚊虫(昆虫)图鉴和防治方法

文章目录 蟑螂形态特征出现源头危害性防治方法 跳蚤形态特征出现源头危害性防治方法 臭虫&#xff0c;又名木蚤、床虱、壁虱形态特征出现源头危害性防治方法 尘螨形态特征出现源头危害性防治方法 蛾蚋&#xff08;ru&#xff09;&#xff0c;又名蛾蠓&#xff08;měng&#xf…

解密.baxia勒索病毒:.baxia勒索病毒的攻击手法及防护建议

导言 在当前网络安全形势日益严峻的背景下&#xff0c;勒索软件的威胁正不断升级&#xff0c;其中.baxia勒索病毒尤为突出。作为一种新型恶意软件&#xff0c;.baxia病毒通过加密用户的文件并要求支付赎金来获取解密密钥&#xff0c;对个人和企业的安全构成了严重威胁。随着其…

医院预约|基于springBoot的医院预约挂号系统设计与实现(附项目源码+论文+数据库)

私信或留言即免费送开题报告和任务书&#xff08;可指定任意题目&#xff09; 目录 一、摘要 二、相关技术 三、系统设计 四、数据库设计 五、核心代码 六、论文参考 七、源码获取 一、摘要 近年来&#xff0c;信息化管理行业的不断兴起&#xff0c;使得人们的日…

国庆节适合买什么东西?精选五款实用又优惠的多功能好物!

临近国庆&#xff0c;我猜很多朋友已经开始为假期做好准备&#xff0c;计划开启出游和购物的节奏了&#xff01;大家都希望在国庆期间&#xff0c;买到一些平时因为价格太贵而舍不得下单的好物&#xff01;作为一名家居兼数码博主&#xff0c;每年国庆的时候我都会疯狂采购各种…

Ansible流程控制-条件语句_循环语句

文章目录 Ansible流程控制条件语句且、或、非、是模糊条件when指令的详细使用方法 循环语句如何使用使用item变量结合with_items或loop指令item变量有固定子元素&#xff1f; 实例-服务器安装基础环境优化需求部分实现换指定新仓库安装基础软件包 Ansible流程控制 一、 1. 条件…

文件服务器FastDFS 消息队列中间件RabbitMQ

新标签页 (chinaunix.net) FastDFS - Browse Files at SourceForge.net 一、FastDFS Tracker和Storage&#xff1a; tracker用来管理所有的storage&#xff0c;只是管理服务器&#xff0c;负责负载均衡。 storage是存储服务器&#xff0c;每一个storage服务器都是一个单独的个…

Cilium + ebpf 系列文章-什么是ebpf?(一)

前言&#xff1a; 这篇非常非常干&#xff0c;很有可能读不懂。 这里非常非常推荐&#xff0c;建议使用Cilium官网的lab来辅助学习&#xff01;&#xff01;&#xff01;Resources Library - IsovalentExplore Isovalents Resource Library, your one-stop destination for ins…

828华为云征文|华为云Flexus云服务器X实例部署Xnote笔记应用

828华为云征文&#xff5c;华为云Flexus云服务器X实例部署Xnote笔记应用 前言一、Flexus云服务器X实例介绍1.1 Flexus云服务器X实例简介1.2 Flexus云服务器X实例特点1.3 Flexus云服务器X实例使用场景 二、Note Mark 介绍2.1 Xnote简介2.2 Xnote特点2.3 主要使用场景 三、本次实…

浅谈剩余电流动作保护装置的功能和应用

【摘要】介绍了剩余电流动作保护装置的组成、类型及功能&#xff0c;并针对设计中存在的问题&#xff0c;提出了在工程应用中需要注意的事项&#xff0c;进而结合相应的规范、标准和应用实际&#xff0c;分析了剩余电流动作保护装置在不同应用场所、不同电气环境下应如何正确选…

数据结构实验二之线性表(中)

实验题3:实现双链表的各种基本运算的算法 题目描述 编写一个程序dlinklist.cpp,实现双链表的各种基本运算和整体建表算法(偏 双链表的元素类型ElemType为int),并在此基础上设计一个程序exp2-3.cpp完成以 功能。 (1)初始化双链表h。 (2)依次采用尾插法插入元素a、b、c、d、e。 …

springboot itextpdf 形式导出pdf

先看效果(这里只设置了软件版本和 完成情况的勾选框) 导入pom依赖 <dependency><groupId>com.itextpdf</groupId><artifactId>itext-asian</artifactId><version>5.2.0</version> </dependency> <!--itextpdf--> <d…

C++之初识STL(概念)

STL&#xff08;标准模板库&#xff09; STL广义分类为&#xff1a;容器&#xff0c;算法&#xff0c;迭代器 * **容器**和**算法**之间通过**迭代器**进行无缝连接 意义&#xff1a;C的**面向对象**和**泛型编程**思想&#xff0c;目的就是**复用性的提升** STL六大组件 1. 容…

Flink 本地启动的多种方式

Flink 本地启动的多种方式 Application模式通过代码提交到Yarn上启动 //设置Yarn客户端 YarnClient yarnClient ; Configuration configuration new Configuration(); if (customConfiguration ! null) {configuration.addAll(customConfiguration); } configuration.set(Jo…