C++———— Vector

一、vector的介绍及使用

1.1 vector的介绍

1.2 vector 的使用

1.21 vector的定义

演示: 

1.22 vector iterator 的使用

 1.begin+end

主要作用:获取第一个数据位置的迭代器和最后一个数据的下一个位置的迭代器。

演示:

2.rbegin+rend

主要作用:rbegin获取最后一个数据位置的迭代器,rend获取第一个数据的前一个位置的迭代器。

演示:

1.23 vector空间增长问题

演示:

 注解:capacity的代码在g++和vs下分别运行下会发现,vs下的capacity是按1.5倍增长的,g++是按2倍增长的,由此可以到底每一次增长多少是不确定的,需要看编译器。

reserve是负责开辟空间的,如果提前知道需要多少空间可以使用reserve统一开辟,减少vector增容的代价问题

resize在开空间时,还会初始化,影响size.

1.23 vector增删查改

演示:  

1.24迭代器失效

迭代器的主要作用就是让算法不用关心底层数据结构,其实本质是指针或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T*。因此迭代器失效,实际就是迭代器底层对应的指针成为了野指针

例子一:

1.会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、 insert、assgin、push_back(由于vector本质底层是数组,地址都是连在一起的,当他需要扩容的时候一般都是,开辟新的空间,并复制需要的值,释放原来的空间,造成如果不更新迭代器,那么迭代器指向的是原来的空间,但是这时候原空间已经被释放了)

做法:更新迭代器

 例子二:指定位置元素的删除操作——erase

 解释:如上图,这段代码崩了,有两个问题,第一个问题是:erase本质其实是覆盖,将该删除位置的数据被后面的数据给覆盖,上面这段代码但他判断一个数据为偶数的时候,会移动后面的数据到要被删除的位置,再++t会错过,移动数据的判断(如下图错过3的判断)。

 第二个错误:当最后一个数据是偶数时,end()的迭代器会-1,it会++,因此错过end()和it的判断,二者一直相等

如果将代码改为如下图所示,就没有以上两个错误了 ,但是仍然报错了

这是因为vs对迭代器的失效检测比较极端,直接认为再erase后的迭代器失效了。再g++下就没有这么严格这段程序就是正确的。

正确改法:erase会返回删除值位置的迭代器

二、vector 的模拟实现

再模拟实现之前,我们可以看一看 vector的原码,发现string不同的是vector的三个成员函数都是迭代器,这里值得注意的是,vector的模拟实现使用的是模板,模板不接受定义和实现分离,所以我们使用一个文件vector.h来实现

1.vector的构造和析构

代码:

vector()
{}
//v2(v1)
vector(vector<T>& v)
{reserve(v.size());for (auto& e : v){push_back(e);}
}//v2 = v1
//现代写法
/*swap(vector<T>& v)
{std::swap(this->_star = v._star);std::swap(this->_finish = v._finish);std::swap(this->_end_of_storage = v._end_of_storage);
}
vector<T>& operator=(vector<T> v)
{swap(v);
}*/
//传统写法
vector<T>& operator=(vector<T> v)
{delete[]_start;_start = _finish = _end_of_storage = nullptr;reserve(v.size());for (auto e : v){push_back(e);}return *this;
}

解析:为什么vector的构造什么东西的没有写,那是因为我们再成员变量给了一个缺省值,作用于初始化列表,vector再构造时会走初始化列表把_start,_finish,_end_of_storage置成nullptr

 能不能不写vector的默认构造?不能,因为拷贝构造也属于构造,如果不写编译器没有办法生成默认构造

能不能不写缺省?不能因为拷贝构造也属于构造,没有默认构造成员变量会变成随机值

赋值有两种写法一种传统一种现代,现代主要利用自定义类型的传值会调用拷贝构造,举个例子:

v1 = v2调用赋值时v2需要传值v,此时调用拷贝构造把v2的值传给v,v中存储的值就是v2的且两者是独立的不涉及浅拷贝,我们把v1与v2的内容交换,v是局部变量,出来作用域就自动释放了,这是v会把原来v1的内容给释放,v1现在的值与v2相同

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

2.end和begin的迭代器

代码:

iterator begin()
{return _start;
}
iterator end()
{return _finish;
}
iterator begin()const
{return _start;
}
iterator end()const
{return _finish;
}
3.resize 

代码:

void reserve(size_t n)
{if (n > capacity()){size_t Oldsize = size();T* tmp = new T[n];for (size_t i = 0; i < Oldsize(); i++){tmp[i] = _start[i];}delete[] _start;_start = tmp;_finsh = _start + Oldsize;_end_of_storage = _start + n;} 
}

 问题分析:为什么不用memcpy

1.memcpy是内存的二进制格式的拷贝,将一段内存空间中的内容原封不动的拷贝到另外一段内存空间中

2.如果拷贝的是内置类型的元素,memcpy即高效又不会出错,但如果拷贝的是自定义类型的元素,并且自定义类型的元素中涉及到资源管理时,就会出错,因为memcpy是浅拷贝。(结论:如果对象中涉及资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃)

4.size capacity 
 
size_t size()
{return _finsh - _start;
}
size_t capacity()
{return _end_of_storage - start;
}
size_t size()const
{return _finsh - _start;
}
size_t capacity()const
{return _end_of_storage - start;
}
5. push_back pop_back

void push_back(const T& x)
{if (_finish == _end_of_storage){reserve(capacity == 0 ? 4 : capacity() * 2);}*_finsh = x;finsh++;
}
void pop_back()
{assert(_finsh > _start);--finsh;
}
6. []

T& operator[](size_t i)
{assert(i < size());return _start[i];
}
const T& operator[](size_t i)const
{assert(i < size());return _start[i];
}
7.insert 和erase

 代码:

void insert(iterator pos, const T& x)
{assert(pos > _start);assert(pos < _finish);if (_finish == _end_of_storage){size_t len = pos - _start;//reserve之后迭代器失效,记录pos的位置,方便更新reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;//更新pos}iterator end = _finish - 1;while (pos <= end){*(end + 1) = *end;end--;}*pos = x;_finish++;
}
iterator erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator end = pos + 1;while (end < _finish){*(end - 1) = *end;end++;}_finish--;return pos;
}

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

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

相关文章

STL入门

STL入门 作者&#xff1a;blue 时间&#xff1a;2024.3 文章目录 STL入门0.概述1.pair2.set(集合)3.vector4.string字符串类型5.queue&#xff0c;deque&#xff0c;priority_queue6.list的用法 0.概述 本文讨论部分常用的STL的运用 1.pair pair是将2个数据组合成一组数据…

洛谷 P10463 Interval GCD Solution

Description 给定序列 a ( a 1 , a 2 , ⋯ , a n ) a(a_1,a_2,\cdots,a_n) a(a1​,a2​,⋯,an​)&#xff0c;有 m m m 个操作分两种&#xff1a; add ⁡ ( l , r , k ) \operatorname{add}(l,r,k) add(l,r,k)&#xff1a;对每个 i ∈ [ l , r ] i\in[l,r] i∈[l,r] 执行 …

从声源定位(DOA)算法仿真到工程源码实现-第八节

一、概述 本节我们记录在respeaker core v2 开发板上部署一个完整的声源定位(DOA)系统&#xff0c;演示可以看第一节中的视频。整个模块可以分为三部分&#xff0c;第一部分为控制开发板上的LED灯显示&#xff0c;这样可以实时的测试算法的效果&#xff1b;第二部分为从ALSA上取…

在linux部署网站

在Linux部署网站&#xff0c;需要准备一个纯净的系统 一、系统环境准备 1.设置静态IP地址 ‌ 2.关闭默认防火墙 systemctl disable firewalld --now ‌ 3.配置SSH密钥登录 4.yum update -y && reboot # 更新系统内核 5.yum install -y wget curl unzip # 安装…

Java后端API限流秘籍:高并发的防护伞与实战指南

目录导航 📜 🛡️ 为什么需要API限流?🧠 主流限流算法大解析👩‍💻 阿里巴巴的限流实践📏 四大黄金定律🤼 限流策略组合拳🏆 限流场景实战💻 技术实现方案🌟 最佳实践分享📈 结语与展望📚 推荐阅读 1. 🛡️ 为什么需要API限流? 在高并发环境中,未…

OpenGL ES 2.0与OpenGL ES 3.1的区别

如果硬件支持且需要更高质量的图形效果&#xff0c;推荐3.1&#xff1b;如果兼容性和开发简便更重要&#xff0c;且效果需求不高&#xff0c;2.0更合适。不过现代车载系统可能越来越多支持3.x版本&#xff0c;所以可能倾向于使用3.1&#xff0c;但具体情况还需调查目标平台的硬…

k8s存储介绍(五)PV与PVC

在 Kubernetes&#xff08;k8s&#xff09;中&#xff0c;持久化存储&#xff08;Persistent Storage&#xff09;是一个非常重要的概念&#xff0c;因为 Pod 本身是无状态的&#xff0c;重启后会丢失数据。为了支持有状态应用&#xff0c;Kubernetes 提供了持久化存储的机制&a…

ORA-00600 [2662]

一、数据库启动报ORA-00600[2662] [oraclenode1 ora11g]$ sqlplus / as sysdbaSQL*Plus: Release 11.2.0.3.0 Production on Thu Dec 22 14:37:00 2011Copyright (c) 1982, 2011, Oracle. All rights reserved.Connected to an idle instance.SQL> startup ORACLE instanc…

WebSocket接入SSL证书

目录 碎碎念解决方法创建 HTTPS WebSocket 服务器创建系统服务启动服务 碎碎念 在访问网站时&#xff0c;使用 HTTPS 非常重要。HTTPS 协议不仅可以确保数据传输的安全性&#xff0c;还可以防止中间人攻击和数据篡改等安全问题。任何没有 SSL 证书的内容都可能会被拒绝访问。因…

c#在work线程中怎样更新UI控件

最近笔者调试修改项目&#xff0c;碰到了c#在work线程中怎样更新UI控件中的场景&#xff0c;简单总结了下&#xff0c;主要有两个方法&#xff1a; 方法1&#xff1a;通过System.Windows.Application.Current.Dispatcher.Invoke来更新UI控件 System.Windows.Application.Curre…

数据结构每日一题day3(顺序表)★★★★★

题目描述&#xff1a;顺序表L的元素递增有序排列&#xff0c;设计一个算法在插入元素x后保持该顺序表仍然递增有序排列,插入成功后返回插入元素所在位置,不成功返回-1 算法思想&#xff1a;在递增有序的顺序表中插入元素 x 并保持有序性&#xff0c;步骤如下&#xff1a; 合法…

MyBatis中mapper.xml 的sql映射规则

一、SQL 映射文件核心元素 MyBatis 映射文件的顶级元素&#xff08;按定义顺序&#xff09;&#xff1a; cache&#xff1a;命名空间的缓存配置。cache-ref&#xff1a;引用其他命名空间的缓存。resultMap&#xff1a;自定义结果集映射。sql&#xff1a;可重用的 SQL 片段。i…

【计算机网络】计算机网络协议、接口与服务全面解析——结合生活化案例与图文详解

协议、接口与服务 导读一、协议1.1 定义1.2 组成 二、接口三、服务3.1 定义3.2 服务与协议的区别3.3 分类3.3.1 面向连接服务于无连接服务3.3.2 可靠服务和不可靠服务3.3.3 有应答服务和无应答服务 结语 导读 大家好&#xff0c;很高兴又和大家见面啦&#xff01;&#xff01;…

Ubuntu服务器中Swapper如何与虚拟内存配合

在Ubuntu服务器中&#xff0c;Swapper和虚拟内存是操作系统中重要的概念&#xff0c;它们共同协作以提高系统的内存管理效率。当物理内存不足时&#xff0c;Swapper会帮助系统将不活跃的数据从内存转移到磁盘上的交换空间(Swap)&#xff0c;以释放内存给需要更多资源的进程。下…

SQL Server 中常见的数据类型及其详细解释、内存占用和适用场景

以下是 SQL Server 中常见的数据类型及其详细解释、内存占用和适用场景&#xff1a; 数据类型类别数据类型解释内存占用适用场景整数类型bigint用于存储范围较大的整数&#xff0c;范围是 -2^63 (-9,223,372,036,854,775,808) 到 2^63-1 (9,223,372,036,854,775,807)8 字节需要…

vue数字公式篇 Tinymce结合使用(二)

继上一篇的数字公式 &#xff0c; 这次的功能是将公式能插入编辑器以及修改 1、Tinymce 自定义 LateX 按钮&#xff0c;打开公式编辑器窗口 LateX.vue window.tinymce.init({...//基础配置这里我就不写了setup(ed) {//自定义 LateX 按钮ed.ui.registry.addButton(LateX, {text:…

python数据增强和转换

数据增强和转换 固定转换随机转换概率控制的转换 固定转换 边缘补充像素(Pad)尺寸变换(Resize)中心截取(CenterCrop)顶角及中心截取(FiveCrop)尺灰度变换(GrayScale) 概率控制的转换 随机垂直翻转(RandomVerticalFlip)随机应用(RandomApply) # -*- coding: utf-8 -*- fro…

Ubuntu下UEFI安全启动安装Nvdia驱动

简介 众所周知&#xff0c;Ubuntu默认使用Nouveau开源驱动&#xff0c;其性能受限&#xff0c;因此我们需要安装Nvidia专用驱动。 安装专用驱动的一般方法非常简单&#xff0c;只需要sudo ubuntu-drivers devices && sudo ubuntu-drivers autoinstall即可&#xff0c…

05_循环结构三目运算符

目录 一、双重for循环 练习 二、break关键字 三、continue 关键字 练习 四、三元运算 / 三目运算 一、双重for循环 外层循环 循环一次&#xff0c;&#xff0c;&#xff0c;内层循环 循环一圈&#xff01;&#xff01;&#xff01; 循环里嵌套循环&#xff1a; for(var…

数据结构初阶-二叉树链式

目录 1.概念与结构 2.二叉数链式的实现 2.1遍历规则 2.2申请内存空间 2.3手动构建一棵二叉树 2.4二叉树结点的个数 2.5二叉树叶子结点的个数 2.6二叉树第K层结点个数 2.7二叉树的高度 2.8二叉树中查找值为x的结点 2.9二叉树的销毁 3.层序遍历 3.1概念 3.2层序遍历…