C++ —— vector 的模拟实现

目录

前言

1. vector深度剖析

 2. 基础框架

3. 核心接口

3.1 reserve

3.2 push_back 和 pop_back

3.3 print

 3.4 insert

3.5 erase

3.6 resize

4. 拷贝构造

4.1 构造与析构

4.2 拷贝构造 

4.3 赋值重载

4.4 迭代器区间

5. 使用memcpy拷贝问题


前言

接:C++ —— 关于vector-CSDN博客icon-default.png?t=O83Ahttps://blog.csdn.net/hedhjd/article/details/142334349?spm=1001.2014.3001.5501


1. vector深度剖析

 

 _start :相当于begin,指向开始的位置
_finish :相当于size,指向最后一个数据的位置
_end_of_storage :相当于_capacity


 2. 基础框架

//因为vector是用模板写的,所以不能进行声明和定义分离,否则会出现链接错误
#pragma once
#include<assert.h>// _start :相当于begin,指向开始的位置
//_finish :相当于size,指向最后一个数据的下一个位置
//_end_of_storage :相当于_capacitynamespace bit
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;//迭代器iterator begin(){return _start;}iterator end(){return _finish;}//const迭代器const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}size_t size() const{return _finish - _start;}size_t capacity() const{return _end_of_storage - _start;}//可读可写T& operator[](size_t i){assert(i < size());return _start[i];}//只读const T& operator[](size_t i) const{assert(i < size());return _start[i];}//判断是否为空bool empty(){return _start == _finish;}private:iterator _start = nullptr;//给个缺省值iterator _finish = nullptr;iterator _end_of_storage = nullptr;};


3. 核心接口

3.1 reserve

//修改之后
//扩容
void reserve(size_t n)
{if (n > capacity()){//提前将旧size赋给新sizesize_t old_size = size();//开辟新空间,创建临时变量//new出来的空间是初始化好的,所以可以直接用T* tmp = new T[n];//memcpy(tmp, _start, old_size * sizeof(T));/*如果T是string或者vector类型,就需要进行深拷贝需要赋值进行拷贝数据,否则使用浅拷贝会导致内存泄漏*/for (size_t i = 0; i < old_size; i++){//赋值就是string/vector的赋值,也就是深拷贝tmp[i] = _start[i];}//释放旧空间delete[] _start;//再把三个私有变量指向新空间_start = tmp;_finish = tmp + old_size;_end_of_storage = tmp + n;}
}


3.2 push_back 和 pop_back

/*T有可能是一个int,也有可能是一个string,还可能是一个vector,
所以需要传&,传&不改变需要加上一个const*/
//尾插
void push_back(const T& x)
{//先判断需不需要扩容if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : capacity() * 2);}//如果不需要就直接插入到_finish的位置*_finish = x;++_finish;
}//尾删
void pop_back()
{assert(!empty());--_finish;
}


3.3 print

如果给出的代码编译器不知道该代码是类型还是变量,所以导致识别不出来,那么可以在前面加上 typename 或者改为 auto 自动推导

//给print_vector套一层模板,让它被谁都可以使用
template<class T>
void print_vector(const vector<T>& v)
{//如果typename在没有实例化的类模板里面取东西,// 那么编译器不能区分这里const_iterator是类型还是静态成员变量//typename vector<T>::const_iterator it = v.begin();//那么使用auto可以自动推导,由v.begin()推导auto it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl;for (auto e : v){cout << e << " ";}cout << endl;
}

将print彻底转换成模板,使所有类型都可以使用,不管是string,vector等等 

//将print彻底转换成模板,使所有类型都可以使用,不管是string,vector等等
template<class Container>
void print_container(const Container& v)
{/*auto it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl;*/for (auto e : v){cout << e << " ";}cout << endl;
}

 3.4 insert

insert在扩容后原来的pos位置就会失效,也就是迭代器失效,就相当于成为了野指针,解决方法是将扩容之前的位置记录下来在扩容后更新pos的位置

//在指定位置插入数据
void insert(iterator pos, const T& x)
{assert(pos >= _start);assert(pos <= _finish);// 扩容if (_finish == _end_of_storage){/*为了防止迭代器失效的情况,先创建一个临时变量len记录扩容之前pos - _start的位置*/size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);//然后再更新pos的位置pos = _start + len;}//将最后一个数据的位置给临时变量enditerator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;
}


3.5 erase

erase之后也会出现迭代器失效,所以需要先记录pos原来的位置然后再更新pos

//删除指定位置的数据
void erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it != end()){*(it - 1) = *it;++it;}--_finish;
}


3.6 resize

用于改变vector的有效长度,也可以用来初始化指定长度的指定数据

三种情况:

1. 当n < size()时,删除多余的元素,直接缩容

2. 当size() < n < capacity()时,直接插入数据

3. 当capacity() < n时,先扩容再插入数据

//用于改变vector的有效长度
//也可以开辟n个空间,使用val去初始化
void resize(size_t n, T val = T())
{//n < size,删除数据if (n < size()){_finish = _start + n;}else//>=size{//扩容reserve(n);while (_finish < _start + n){//插入数据val*_finish = val;++_finish;}}
}


4. 拷贝构造

4.1 构造与析构

//默认构造vector(){}// C++11 强制生成默认构造//vector() = default;//析构~vector(){if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}}


4.2 拷贝构造 

//拷贝构造vector(const vector<T>& v){reserve(v.size());for (auto& e : v){push_back(e);}}


4.3 赋值重载

//赋值重载的传统写法
//clear清除数据但是不释放空间
void clear()
{_finish = _start;
}// v1 = v3
vector<T>& operator=(const vector<T>& v)
{if (this != &v){clear();reserve(v.size());for (auto& e : v){push_back(e);}}return *this;
}//赋值重载的现代写法
void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);
}// v1 = v3
vector<T>& operator=(vector<T> v)
{swap(v);return *this;
}


4.4 迭代器区间

1. 类模版里的成员函数还能继续是函数模版

2. 在迭代器区间构造的函数时可以使用函数模版,这样可以使用任意容器的迭代器初始化,例如链表

//迭代器区间
// 类模板的成员函数,还可以继续是函数模版
//加上一个模板那么迭代器就可以是任意类型的迭代器
//需要推导
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);++first;}
}//可以直接使用size_t类型  使用n个val初始化
vector(size_t n, const T& val = T())
{reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}
}//可以直接使用int类型  使用n个val初始化
vector(int n, const T& val = T())
{reserve(n);for (int i = 0; i < n; i++){push_back(val);}
}


5. 使用memcpy拷贝问题

memcpy拷贝问题的问题其实就是浅拷贝的原因

浅拷贝就是两个指针指向同一块空间,如果一个指针对该空间进行释放或者其他操作就会影响另一个指针导致内存泄漏等问题

所以最好使用深拷贝让两指针指向不同的空间然后使其数据保持一致

错误代码 

//扩容//错误代码void reserve(size_t n){if (n > capacity()){//提前将旧size赋给新sizesize_t old_size = size();//开辟新空间,创建临时变量//new出来的空间是初始化好的,所以可以直接用T* tmp = new T[n];//将旧空间里的数据拷贝给新空间//将 _start指向的size() * sizeof(T)空间里的数据拷贝到临时变量tmp里去memcpy(tmp, _start, size() * sizeof(T));//释放旧空间delete[] _start;//再把三个私有变量指向新空间_start = tmp;_finish = tmp + old_size;_end_of_storage = tmp + n;}}

修改之后

//修改之后
//扩容
void reserve(size_t n)
{if (n > capacity()){//提前将旧size赋给新sizesize_t old_size = size();//开辟新空间,创建临时变量//new出来的空间是初始化好的,所以可以直接用T* tmp = new T[n];//memcpy(tmp, _start, old_size * sizeof(T));/*如果T是string或者vector类型,就需要进行深拷贝否则使用浅拷贝会导致内存泄漏*/for (size_t i = 0; i < old_size; i++){//赋值就是string/vector的赋值,也就是深拷贝tmp[i] = _start[i];}//释放旧空间delete[] _start;//再把三个私有变量指向新空间_start = tmp;_finish = tmp + old_size;_end_of_storage = tmp + n;}
}

感谢观看~

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

惊艳桌面时钟软件 为你的桌面打造专属时间管理!

在快节奏的现代生活中&#xff0c;时间是最宝贵的资源之一。无论是在工作还是生活中&#xff0c;我们都需要时刻关注时间&#xff0c;在桌面显示一个时钟&#xff0c;可以让你更方便的掌握时间。芝麻时钟 &#xff08;下载地址&#xff1a;https://clock.zhimasoft.cn/?bili&a…

linux服务器安装原生的php环境

在CentOS上安装原生的PHP环境相对简单。下面是一个详细的步骤指南&#xff0c;适用于CentOS 7及更高版本。 ### 第一步&#xff1a;更新系统 首先&#xff0c;确保你的系统是最新的&#xff1a; sudo yum update -y ### 第二步&#xff1a;安装EPEL和Remi仓库 1. **安装EP…

分布式数据库——HBase基本操作

启动HBase: 1.启动hadoop,进入hadoop的sbin中 cd /opt/hadoop/sbin/ 2.初始化namenode hdfs namenode -format 3.启动hdfs ./start-all.sh 4.启动hbase cd /opt/hbase/bin ./start-hbase.sh 5.使用jps查看进程 jps 以下图片则是hbase启动成功~ 运行HBase ./hbase sh…

【教学类-52-14】20240925动物数独(N宫格通用版)1图、2图、6图、有答案、无答案 组合版18套

背景需求&#xff1a; 制作了3-5宫格&#xff08;1、2、6图&#xff09;样式18组&#xff0c;它们用的都是&#xff08;1、2、6图&#xff09;的word模板&#xff0c;只是宫格数量不同&#xff0c;图片插入大小不同&#xff0c;是否可以做一个通用代码&#xff1f; 【教学类-…

13.第二阶段x86游戏实战2-动态模块地址

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要…

鸿蒙OpenHarmony【轻量系统芯片移植】轻量系统STM32F407芯片移植案例

轻量系统STM32F407芯片移植案例 介绍基于STM32F407IGT6芯片在拓维信息[Niobe407]开发板上移植OpenHarmony LiteOS-M轻量系统&#xff0c;提供交通、工业领域开发板解决方案。移植架构采用Board与SoC分离方案&#xff0c;使用arm gcc工具链Newlib C库&#xff0c;实现了lwip、l…

2024.9.25 作业和思维导图

栈 #include <iostream> #include <stdexcept> using namespace std;class My_stack { private:int * data; //栈空间int capacity;int top; //栈顶元素的下标 protected:public:/******************成员函数*************///构造函数My_stack(int c 10):capac…

利士策分享,动摇时刻的自我救赎

利士策分享&#xff0c;动摇时刻的自我救赎 在人生的长河中&#xff0c;我们每个人都会面临各种挑战与抉择&#xff0c; 那些让人心生动摇的瞬间&#xff0c;如同夜空中偶尔掠过的乌云&#xff0c;遮蔽了前行的星光。 但正是这些动摇&#xff0c;构成了我们成长的轨迹&#x…

C++/CLI编程知识点小记

1.前言 本篇博文并非详细的C/CLI教程&#xff0c;仅是博主就学习和实践总结的部分知识点记录。 第一次接触C/CLI是2017年了&#xff0c;用C编写底层库&#xff0c;C/CLI编写wrapper层&#xff0c;在C#项目中进行调用&#xff0c;开发应用。 2.内容 C/CLI是一种混合编程&…

使用Maven创建一个Java项目并在repository中使用

JDK环境&#xff1a;1.8.0_371 Maven环境 &#xff1a;Apache Maven 3.6.3 配置完成jdk和mvn后&#xff0c;进入到指定文件夹下执行如下语句&#xff1a; mvn archetype:generate -DgroupIdtop.chengrongyu -DartifactIdCyberSpace -DarchetypeArtifactIdmaven-archetype-quic…

Android开发okhttp下载图片带进度

Android开发okhttp下载图片带进度 下载网络图片的方法有很多&#xff0c;这次介绍写用okhttp来下载网络图片&#xff0c;主要我看中的是用okhttp下载有进度返回&#xff0c;提示下用户 一、思路&#xff1a; 用OkHttpClient().newCall(request) 二、效果图&#xff1a; 三、…

AI大模型助力数据消费,构建数据飞轮科学、高效的体系

随着互联网的技术高速发展&#xff0c;越来越多的应用层出不穷&#xff0c;伴随着数据应用的需求变多&#xff0c;为快速响应业务需求&#xff0c;很多企业在初期没有很好的规划的情况下&#xff0c;存在不同程度的烟囱式的开发模式&#xff0c;这样会导致企业不同业务线的数据…

分布式Id生成策略-美团Leaf

之前在做物流相关的项目时候&#xff0c;需要在分布式系统生成运单的id。 1.需求&#xff1a; 1.全局唯一性&#xff1a;不能出现重复的ID。&#xff08;基本要求&#xff09; 2.递增&#xff1a;大多数关系型数据库&#xff08;如 MySQL&#xff09;使用 B 树作为索引结构。…

大数据 flink 01 | 从零环境搭建 简单Demo 运行

什么是Flink Flink是一个开源的流处理和批处理框架,它能够处理无界和有界的数据流&#xff0c;具有高吞吐量、低延迟和容错性等特点 Flink 可以应用于多个领域如&#xff1a;实时数据处理、数据分析、机器学习、事件驱动等。 什么是流式处理&#xff1f;什么是批处理 流处理…

吴师兄:非科班程序员,创作出Github标星75.3K的宝藏项目,一周爆火……

这是《开发者说》的第18期&#xff0c;今天我们采访的是在Github上传LeetCode动画题解&#xff0c;获得75.3K标星宝藏项目的程序员吴师兄。 吴师兄从985大学毕业&#xff0c;从通信工程外包零基础转码程序员&#xff0c;逐渐进入一些中厂和大厂&#xff0c;工资也从三四千起步…

Elasticsearch——介绍、安装与初步使用

目录 1.初识 Elasticsearch1.1.了解 ES1.1.1.Elasticsearch 的作用1.1.2.ELK技术栈1.1.3.Elasticsearch 和 Lucene1.1.4.为什么不是其他搜索技术&#xff1f;1.1.5.总结 1.2.倒排索引1.2.1.正向索引1.2.2.倒排索引1.2.3.正向和倒排 1.3.Elasticsearch 的一些概念1.3.1.文档和字…

C++之stack 和 queue

目录 前言 1.stack的介绍和使用 1.1 stack的介绍 1.2 stack的使用 1.3 stack 的模拟 2. queue的介绍和使用 2.1 queue的介绍 2.2 queue的使用 2.3 queue的模拟 3.适配器 3.1 什么是适配器 3.2 STL标准库中stack和queue的底层结构 3.3 deque 的介绍&#xff08;了解&…

智慧城市主要运营模式分析

(一)运营模式演变 作为新一代信息化技术落地应用的新事物,智慧城市在建设模式方面借鉴了大量工程建设的经验,如平行发包(DBB,Design-Bid-Build)、EPC工程总承包、PPP等模式等,这些模式在不同的发展阶段和条件下发挥了重要作用。 在智慧城市发展模式从政府主导、以建为主、…

一日连发两款视频大模型,火山引擎杀疯了!

9月24日&#xff0c;字节跳动旗下火山引擎在深圳举办AI创新巡展&#xff0c;并首次对外发布豆包视频生成-PixelDance、豆包视频生成-Seaweed两款AI大模型&#xff0c;并公布了多项AI大模型的全新升级&#xff0c;以一种全新的姿态迎接AI时代的到来。 雷科技此次受邀参与巡展&a…

David律所代理Jose Martin幽默水果版权首发维权,尚未TRO

案件基本情况&#xff1a;起诉时间&#xff1a;2024/9/18案件号&#xff1a;2024-cv-08484原告&#xff1a;Jose Martin原告律所&#xff1a;David起诉地&#xff1a;伊利诺伊州北部法院涉案商标/版权&#xff1a;原告品牌简介&#xff1a;西班牙的卓越艺术家Jose Martin以他非…