【STL】之 vector 使用方法及模拟实现

前言:

本文主要讲在C++ STL库中vector容器的使用方法和底层的模拟实现~

成员变量的定义:
对于vector容器,我们首先采用三个成员变量去进行定义,分别是:

    private:iterator _start; // 指向数据块的开始iterator _finish; // 指向有效数据的 下一个 位置!!!iterator _endOfStorage; // 指向存储容量的尾

迭代器的书写:

因为vector底层存储空间是连续的,我们可以直接用指针作为迭代器,分别是const和非const类型。注意我们需要用类模板去定义

      // 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;}

构造函数:

我们使用初始化列表进行初始化。

        vector():_start(nullptr), _finish(nullptr),_endOfStorage(nullptr){}

拷贝构造函数:

对于自定义类型,并且需要深拷贝的情况下,需要自己书写拷贝构造函数,编译器默认生成的

拷贝构造函数只适用于浅拷贝,或者内置类型。

       //深拷贝需要自己写一份拷贝构造函数vector(const vector<T>& v){_start = new T[v.capacity()];memcpy(_start, v._start, v.size() * sizeof(T));_finish = _start + v.size();_endOfStorage = _start + v.capacity();}

赋值重载函数:

赋值重载同样也需要深拷贝,这里我们采用现代的写法,比较简洁。

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

析构函数:

利用delete去释放空间,并且将内部成员变量赋值为空指针。

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

常用的size() 和 capacity()函数接口

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

reserve函数:

reserve函数主要是用来扩容开空间,并且对新开辟的空间不做初始化。

我们一般需要将原来的空间进行析构,在析构前需要将原本位置的值拷贝给新的空间

        void reserve(size_t n){size_t len = _finish - _start;if (n > _endOfStorage - _start){T* tmp = new T[n];//memcpy(tmp, _start, sizeof(T) * len); 注意memcpy和memove是浅拷贝for (int i = 0; i < len; i++){//tmp[i] = _start[i];*(tmp + i) = *(_start + i);}delete[] _start;//注意要将原来的析构_start = tmp;_finish = _start + len;_endOfStorage = _start + n;}}

resize函数:

resize函数适用于开空间和缩容,一般是用来扩容,那么这个函数和reserve有什么区别呢?

最大的区别就是resize会将新开辟的空间进行初始化,而reserve对于新开辟的空间不做任何处理

        void resize(size_t n, const T& value = T()){if (n > size()){//先判断是否需要扩容if (n > capacity()){reserve(n);}for (int i = 0; i < n-size(); i++){*(_finish + i) = value;}_finish += n - size();}else{_finish = n + _start;}}

push_back()函数

此函数接口主要用来尾插。实现与用法比较简单。

        void push_back(const T& x){//判断是否需要扩容if (_finish == _endOfStorage){size_t capacity = _endOfStorage - _start;reserve(capacity == 0 ? 4 : capacity * 2);}*_finish = x;//注意解引用_finish++;}

pop_back()函数

这个函数与push_back()相对应,进行尾删。

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

swap交换函数

顾名思义就是将两个vector容器内容以及大小进行交换,这里我们直接借用std标准库的swap函数实现。

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

insert()

插入函数,我们可以在任意位置进行插入,注意函数的第一个参数pos并不是下标值,而是迭代器!

这里的插入如果需要扩容会导致怕pos的迭代器失效问题!

        iterator insert(iterator pos, const T& x)//注意第一个参数是迭代器!{assert(pos >= _start);assert(pos < _finish);size_t capacity = _endOfStorage - _start;//先判断扩容if (_finish == _endOfStorage){size_t len = pos - _start;size_t newcapacity = capacity == 0 ? 4 : capacity * 2;reserve(newcapacity);pos = len + _start;//需要重新确定pos的位置}//不要浅拷贝//memove(_start + pos + 1, _start + pos, sizeof(T) * (size() - pos));iterator tmp = _finish;while (tmp != pos){*tmp = *(tmp - 1);tmp--;}*pos = x;++_finish;return pos;}

erase函数

这个函数主要用来将任意位置的值进行删除,这里的第一个参数同样也是迭代器!

        //注意参数是迭代器iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator tmp = pos;while (tmp < _finish){*tmp = *(tmp + 1);tmp++;}_finish--;return pos;}

关于vector容器使用和实现的注意点:

1、迭代器失效

如果调用insert函数,并且扩容的话,原先pos迭代器就会失效,因为pos迭代器指向的是原来空间的pos位置,但是经过扩容之后,原先的空间会被销毁,生成新的空间,所以我们需要先记录迭代器在空间的相对位置,这样方便在扩容后创建的新空间不会失效。

所以我们尽量不要使用insert和erase过后的迭代器 insert和erase 形参pos都可能会失效

2、reserve 不能使用memcpy/memove的原因 (深层次的深拷贝的问题)

memcpy 和 memove都是浅拷贝!!!对于自定义类型的要求的深拷贝,memcpy和memove只将值拷贝了下来,我们要用赋值就是深拷贝。

对于内置类型,或者对于只需要浅拷贝的自定义类型,我们使用memove没有任何问题。

但是针对于自定义类型需要深拷贝的情况,我们不能使用memcpy或者memove,因为这两个都只能解决浅拷贝的问题。

比如这里的_str如果只是浅拷贝。没有创建空间,那么在delete之后,这段空间就已经销毁了,所以此时的tmp就是野指针,因此我们需要用赋值进行深拷贝! 

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

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

相关文章

React类组件生命周期与this关键字

类组件生命周期 参考链接 一图胜千言&#xff08;不常用的生命周期函数已隐藏&#xff09; 代码&#xff1a; //CC1.js import { Component } from "react";export default class CC1 extends Component {constructor(props) {super(props);console.log("con…

【Vue3】watchEffect

【Vue3】watchEffect 背景简介开发环境开发步骤及源码 背景 随着年龄的增长&#xff0c;很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来&#xff0c;技术出身的人总是很难放下一些执念&#xff0c;遂将这些知识整理成文&#xff0c;以纪念曾经努力学习奋斗的日子。本文…

C++初学(7)

7.1、字符串 字符串是存储在内存的连续字节中的一系列字符。C处理字符串的方式有两种&#xff0c;第一种是来自C语言&#xff0c;常被称为C风格字符串&#xff0c;另一种则是基于string类库的方法。 存储在连续字节中的一系列字符意味着可以将字符存储在char数组中&#xff0…

微信小程序开发 快速学习 这篇就够了

目录 一、配置篇 &#xff08;1&#xff09;官网链接&#xff1a; &#xff08;2&#xff09;项目分析 &#xff08;3&#xff09;调试器 &#xff08;4&#xff09;预览体验 &#xff08;5&#xff09;配置文件 &#xff08;6&#xff09;配置pages &#xff08;7&…

CSRF Token 原理

CSRF 攻击 CSRF 攻击成功的关键是&#xff0c;恶意网站让浏览器自动发起一个请求&#xff0c;这个请求会自动携带 cookie &#xff0c;正常网站拿到 cookie 后会认为这是正常用户&#xff0c;就允许请求。 防范 如果在请求中加一个字段&#xff08;CSRF Token&#xff09;&am…

鸿蒙开发—黑马云音乐之Music页面

目录 1.外层容器效果 2.信息区-发光效果 3.信息区-内容布局 4.播放列表布局 5.播放列表动态化 6.模拟器运行并配置权限 效果&#xff1a; 1.外层容器效果 Entry Component export struct MuiscPage {build() {Column() {// 信息区域Column() {}.width(100%)// .backgroun…

kubernetes管理GUI工具Lens

从github上可以知道&#xff0c;lens的前端是用electron做的客户端工具&#xff0c;打开安装路径你会发现kubectl.exe,没错&#xff0c;就是你经常用的kubectl命令行的客户端工具。kubectl本来就能输出json的数据类型&#xff0c;集成前端更方便了。看到这里你是不是发现&#…

Linux云计算 |【第二阶段】AUTOMATION-DAY5

主要内容&#xff1a; YAML语法格式&#xff0c;层级关系、Ansible Playbook文件及语法格式、Ansible变量&#xff08;定义变量方法、优先级顺序、setup和debug查看变量&#xff09; 补充&#xff1a;Ansible ad-hoc 可以通过命令行形式远程管理其他主机&#xff0c;适合执行一…

视频逐帧播放查看神器-android闪黑闪白等分析辅助工具

背景 刚好有学员朋友在群里问道有没有什么播放软件可以实现对视频的逐帧即一帧一帧播放。在做android系统开发时候经常会偶尔遇到有时候是闪黑&#xff0c;闪白等一瞬间现象的问题。这类问题要分析的话就不得不需要对设备录屏&#xff0c;然后对录屏进行逐帧播放查看现象&…

2020真题-架构师案例(五)

问题1&#xff08;13分&#xff09; 针对该系统的功能&#xff0c;孪工建议采用管道-过滤器&#xff08;pipe and filter&#xff09;的架构风格&#xff0c;而王工则建议采用仓库&#xff08;reposilory&#xff09;架构风格。满指出该系统更适合采用哪种架构风格&#xff0c…

【C++题解】1581. 马里奥的银币1

问题&#xff1a;1581. 马里奥的银币1 类型&#xff1a;数组找数 题目描述&#xff1a; 马里奥有很多银币&#xff0c;有一天他得到了一张魔法卡&#xff0c;只要使用这张魔法卡&#xff0c;就可以使得他的银币里面的最大的银币金额变得更大。如果他最大的银币是偶数的金额&a…

获取正版免费的xshell

1&#xff0c;安装 xshell官网 打开xshell官网站点&#xff1a;NetSarang Homepage CN - NetSarang Website 请认准&#xff0c;百度的xshell中文网都是要收费的 1&#xff0c;点击 xshell的下载 点击进入xshell的下载页面&#xff0c;或者直接访问所有下载 - NetSarang Webs…

实验2-4-2 求N分之一序列前N项和**注意小细节

//实验2-4-2 求N分之一序列前N项和//计算序列 1 1/2 1/3 ... 的前N项之和。#include<stdio.h> #include<math.h> int main(){int N;double sum0.0;scanf("%d",&N);for(int a1;a<N;a)sum(1.0/a);//这里必须是1.0 不可以是1&#xff01;&#x…

VirtualBox创建共享磁盘

VirtualBox创建共享磁盘 目录 VirtualBox创建共享磁盘1、划分共享磁盘1.1、【管理】->【工具】->【虚拟介质管理】1.2、【创建】->【VDI&#xff08;VirtualBox 磁盘映像&#xff09;】->【下一步】1.3、【预先分配全部空间】->【下一步】1.4、【分配大小】->…

网络协议二 : 使用Cisco Packet Traceer工具模拟网络环境,集线器,网桥,交换机,路由器,IP,同一网段

1. 安装 Cisco Packet Tracer baidu 网盘地址&#xff0c;感谢大神分享 安装&#xff0c;破解&#xff0c;中文化&#xff0c;都有说明&#xff0c;建议使用7.x的那个版本&#xff0c;感觉比8.x的翻译要完整一点 https://pan.baidu.com/s/18iWBOfhJJRhqgQqdNQcfMQ?pwddcch#…

什么是技术作家风格指南?

技术写作风格指南旨在提供必要的格式风格&#xff0c;以帮助技术作家为读者创建引人入胜且一致的内容。然而&#xff0c;技术写作与普通的自由写作有很大不同。目的是将复杂的技术主题分解为易于理解的内容&#xff0c;以帮助读者了解如何使用产品或服务。 在本文中&#xff0…

FreeModbus学习——eMBInit初始化

FreeModbus版本&#xff1a;1.6 在mb.c文件中 先看一下静态变量的定义 /* ----------------------- Static variables ---------------------------------*/static UCHAR ucMBAddress; static eMBMode eMBCurrentMode;ucMBAddress是从机地址&#xff0c;eMBCurrentMode是M…

C++ 八股(2)

1.函数调用的参数是以什么顺序压栈的&#xff0c;为什么&#xff1f; 从右向左压栈的。因为C, C支持可变参函数。 可变参函数就是参数个数可变的函数&#xff0c;如printf()就是可变参函数 void func(int a,...){} 2.有一个函数 在main函数中通过&#xff1a;string s fun…

焦化超低排放解决方案

在环保政策日益严苛与可持续发展理念深入人心的当下&#xff0c;朗观视觉小编认为焦化行业作为传统重工业的重要组成部分&#xff0c;正经历着一场前所未有的绿色变革。其中&#xff0c;“焦化超低排放”不仅是对环境保护的积极响应&#xff0c;更是行业转型升级、实现高质量发…

【开发问题记录】启动某个微服务时无法连接到seata(seata启动或配置异常)

问题记录 一、问题描述1.1 问题复现1.1.1 将Linux中的部分微服务启动1.1.2 在本地启动当时出错的服务 1.2 解决思路1.2.1 Nacos中seata相关的信息1.2.2 Linux中seata相关的信息 二、问题解决2.1 seata的配置错误2.1.1 Nacos中seata的配置问题2.1.2 命名空间问题的发现 2.2 网络…