C++STL之vector

vector

1. vector介绍

  • vector文档
  • vector其实就是一个顺序表,它表示可变大小数组的序列容器。
  • 像数组一样,可以使用下标+[] 来访问vector的元素,和数组一样高效;甚至,它的大小是可以动态改变的,其大小由容器自动处理。
  • 在底层上,vector使用动态分配空间来储存元素。当新元素插入,原空间不够时,需要重新分配一块连续的空间来增加存储空间,做法是开辟大一点的空间,然后将原内容拷贝过来,再释放原空间,此举的时间成本相对较高。
  • vector会额外分配一些空间,以适应可能的增长,实际的存储空间可能比需要的空间更大。不同的STL库的实现采取不同的策略。
  • vector的成员变量和string不同,string是两个整型存size和capacity,一个char指针指向动态开辟的空间;而vector是三个迭代器(底层类似于指针),分别是开头的迭代器、最后一个元素下一个位置的迭代器、开辟的空间的最末端的迭代器。
  • string是管理字符串的类,那么vector< char>实例化为char是否能替代string呢?
    当然不可以,因为string后都有’\0’,可以更好和C的字符串对接,另外string的接口也更加丰富,可以更好的管理字符串。

2. vector的常用接口

vector的许多接口中有很多别名:
在这里插入图片描述

相比于string,vector的接口数量就显得很少了,下面我们看看vector常用的接口。

  1. 构造函数(constructor)

    (constructor)功能
    explicit vector (const allocator_type& alloc = allocator_type());默认构造函数
    explicit vector (size_type n, const value_type& val = value_type(), const allocator_type& alloc = allocator_type());用n个val值初始化
    template <class InputIterator> vector (InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type());用迭代器初始化(可以允许其他类型作为参数初始化)
    vector (const vector& x);拷贝构造函数
    vector<int> v1;
    vector<int> v2(3, 2);
    int nums[] = { 1,2,3 };
    vector<int> v3(arr, arr + 3);
    vector<int> v4(v3);
    
  2. 容量操作

    函数功能
    size_type size(); const返回有效元素个数
    size_type capacity(); const返回实际空间大小
    bool empty(); const判断是否为空
    void resize (size_type n, value_type val = value_type());改变有效元素个数
    void reserve (size_type n);改变空间大小

    补充:

    1. resize:
      当n小于有效元素个数时,会将n之后的所有元素删除,只保留从头到n位置的元素;
      当n大于有效元素个数,却小于实际空间大小时,会在最后一个有效元素后填充值val,直到n位置;
      当n大于实际空间大小时,会开辟空间,然后在最后一个有效元素后填充值val,直到n位置。
    2. reserve:
      当n大于实际空间大小时,开辟空间。其余任何情况不做处理。
  3. 迭代器

    函数功能
    iterator begin();
    const_iterator begin() const;
    返回容器开头的位置的迭代器
    iterator end();
    const_iterator end() const;
    返回容器最后一个有效元素的下一个位置的迭代器
    reverse_iterator rbegin();
    const_reverse_iterator rbegin() const;
    返回容器最后一个有效元素的位置的迭代器
    reverse_iterator rend();
    const_reverse_iterator rend() const;
    返回容器开头的前一个位置的迭代器

    在这里插入图片描述

  4. 访问元素

    函数功能
    reference operator[] (size_type n);
    const_reference operator[] (size_type n) const;
    访问下标为n的元素
    reference at (size_type n);
    const_reference at (size_type n) const;
    访问下标为n的元素
    vector<int> v(3, 2);
    cout << v[0] << endl;
    cout << v.at(1) << endl;
    
  5. 修改操作

    函数功能
    void push_back (const value_type& val);尾插一个值
    void pop_back();尾删一个值
    insert在某个位置插入元素
    erase删除某个位置的元素
    void swap (vector& x);交换两个vector对象
    void clear();清空有效元素

3. 模拟实现vector类(部分接口)

在这里插入图片描述

#include<iostream>
#include<assert.h>using namespace std;namespace Myspace
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;vector(){ }vector(size_t n, const T& value = T()){// 开空间_start = new T[n];_finish = _start + n;_endofstorage = _finish;// 放数据for (auto& e : *this){e = value;}}vector(int n, const T& value = T()){// 开空间_start = new T[n];_finish = _start + n;_endofstorage = _finish;// 放数据for (auto& e : *this){e = value;}}vector(long n, const T& value = T()){// 开空间_start = new T[n];_finish = _start + n;_endofstorage = _finish;// 放数据for (auto& e : *this){e = value;}}template<typename InputIterator>vector(InputIterator first, InputIterator last){/*int len = last - first;_start = new T[n];_finish = _start + len;_endofstorage = _finish;for (auto& e : *this){e = *first;first++;}*/_start = new T[last - first];for (size_t i = 0; i < last - first; i++){_start[i] = first[i];}_finish = _start + (last - first);_endofstorage = _start + (last - first);}vector(const vector& v){_start = new T[v.capacity()];for (size_t i = 0; i < v.size(); i++){_start[i] = v._start[i];}_finish = _start + v.size();_endofstorage = _start + capacity();}~vector(){if (_start){delete[] _start;_start = _finish = _endofstorage = nullptr;}}vector<T>& operator= (vector<T> v){swap(v);return *this;}//----------------------------------  迭代器  ---------------------------------------//iterator begin(){return _start;}const_iterator begin() const{return _start;}iterator end(){return _finish;}const_iterator end() const{return _finish;}//----------------------------------  容量  ---------------------------------------//size_t size() const{return _finish - _start;}size_t capacity() const{return _endofstorage - _start;}bool empty() const{return _start == _finish;}void reserve(size_t n){if (n > capacity()){int len = size();iterator tmp = new T[n];// 这里拷贝数据不能用memcpy,如果T需要深拷贝,memcpy只是浅拷贝if (_start){for (size_t i = 0; i < n; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + len;_endofstorage = _start + n;}}void resize(size_t n, const T value = T()){if (n <= size()){_finish = _start + n;}else{reserve(n);while (_finish != _start + n){*_finish = value;_finish++;}}}//----------------------------------  修改  ---------------------------------------//void push_back(const T value){if (_finish == _endofstorage){reserve(_start == _endofstorage ? 4 : capacity() * 2);}*_finish = value;++_finish;}void pop_back(){assert(_start != _finish);--_finish;}iterator insert(iterator pos, const T& value){assert(pos >= _start && pos <= _finish);if (_finish == _endofstorage){int len = pos - _start; // 防止扩容后迭代器失效reserve(_start == _endofstorage ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}_finish++;*pos = value;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;}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}private:iterator _start = nullptr;	// 给缺省值,在构造函数的初始化列表中自动初始化iterator _finish = nullptr;iterator _endofstorage = nullptr;};
}

注意:

  1. 构造函数vector(int n, const T& value = T())接口需要重载多个(int/size_t/long),以防止创建对象时(vector<int> v(2,3);)编译器自动匹配到vector(InputIterator first, InputIterator last)这个接口。
    原因:编译器总会选择最匹配的接口。
  2. 在扩容时拷贝数据的时候,不要使用memcpy(上述代码的第151行),原因如下:
    如果模板实例化为string,那么此时就相当于memcpy(string1, string2, size),将两个string类memcpy,一定是浅拷贝,因为string类本身有个char*的指针,需要动态开辟空间,memcpy仅仅是将两个指针指向了同一块空间,并没有开辟新的空间。
    直接赋值即可,赋值会调用string自己的赋值运算符重载,它自己会实现深拷贝。
    不止是string,其他任何动态管理空间的类都是如此。

4. 迭代器失效

  1. 迭代器的作用:
    迭代器就是为了不管各个容器的底层如何实现,都能够使用算法。其底层实际是个指针,或是对指针的封装,比如string和vector的迭代器就是char* 和 T*。

  2. 迭代器失效:
    当迭代器底层所指向的空间被销毁了,还依旧使用该迭代器,那么就会造成野指针的问题,后果是程序崩溃。在VS2022下直接报错崩溃,在Linux下可能不会报错,因此对于程序员来说,避免迭代器失效是必须的。

  3. 可能引起迭代器失效的场景:

    1. 扩容操作
    #include <iostream>
    using namespace std;
    #include <vector>
    int main()
    {vector<int> v{1,2,3,4,5,6};auto it = v.begin();v.resize(100, 8);v.reserve(100);v.insert(v.begin(), 0);v.push_back(8);v.assign(100, 8);while(it != v.end()){cout<< *it << " " ;++it;}cout<<endl;return 0;
    }
    

    以上五个接口可能会导致迭代器it失效,原因:
    使用接口改变底层空间时,可能会扩容,而vector的扩容逻辑是:开辟一块新空间,将原数据拷贝至新空间,然后释放旧空间。扩完容之后底层地址空间就变了,而外部的迭代器it依旧指向原来已经被释放的空间,对迭代器再次操作时,就是对已经释放的空间进行操作,会引起代码奔溃。

    1. 删除指定位置元素 — erase
    #include <iostream>
    using namespace std;
    #include <vector>
    int main()
    {int a[] = { 1, 2, 3, 4 };vector<int> v(a, a + sizeof(a) / sizeof(int));// 使用find查找4所在位置的iteratorvector<int>::iterator pos = find(v.begin(), v.end(), 3);// 删除pos位置的数据,导致pos迭代器失效。v.erase(pos);cout << *pos << endl; // 此处会导致非法访问return 0;
    }
    

    上述代码导致迭代器失效的原因:
    erase删除pos位置的元素,后面的元素会往前挪动,理论上没有产生扩容,底层地址空间就不会改变,迭代器不应该失效。但是如果pos位置是最后一个元素,删除之后,pos位置就成了end(),是最后一个有效元素的下一个位置,此位置不属于有效数据的区间,此迭代器就失效了。在VS中再对pos迭代器进行操作,程序就会奔溃。

  4. 解决办法:
    完成扩容或删除操作之后,给迭代器重新赋值即可。

  5. Linux下,g++编译器对迭代器失效的检测并不是很严格,处理也没有VS下那么极端。

    1. vs下迭代器失效
      在这里插入图片描述

    2. g++下迭代器失效

      在这里插入图片描述

    由此可见,SGI版本的STL(Linux下的g++编译),迭代器失效后代码不一定会奔溃(如果迭代器不在begin和end的范围内也会奔溃),但是它的结果一定不正确。

  6. 不仅vector存在迭代器失效的问题,string也有迭代器失效的问题,因此我们在使用STL时,一定要小心迭代器失效!

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

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

相关文章

软考55-上午题-【数据库】-数据库设计步骤1

一、数据库设计的步骤 新奥尔良法&#xff0c;四个主要阶段&#xff1a; 1、用户需求分析&#xff1a;手机用户需求&#xff0c;确定系统边界&#xff1b; 2、概念设计&#xff08;概念结构设计&#xff09;&#xff1a;是抽象概念模型&#xff0c;较理想的是采用E-R方法。 …

代码随想录算法训练营第七天

● 自己看到题目的第一想法 第454题.四数相加II 方法&#xff1a; 方法一&#xff1a; 暴力法 思路&#xff1a; 注意&#xff1a; 代码&#xff1a; class Solution { public:int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<i…

QT 网络编程 8

1 基础知识 udp tcp 2 UDP 框架 客户端: QUdpSocket x; qint64 writeDatagram( const char *data, qint64 size, const QHostAddress &address, quint16 port );服务器: void Server::initSocket(){udpSocket new QUdpSocket(this);udpSocket->bind(QHostAddress…

macos jupyter notebook字体的修改

终端codemirror 记事本打开 搜索font-family 修改font-size保存即可

重学SpringBoot3-@ConditionalOnXxx条件注解

重学SpringBoot3-ConditionalOnXxx条件注解 引言常见的条件注解常见的条件注解示例扩展条件注解1. ConditionalOnJndi2. ConditionalOnJava3. ConditionalOnCloudPlatform4. ConditionalOnEnabledResourceChain5. 自定义条件注解 总结 引言 Spring Boot 提供了一组强大的条件注…

ERDAS监督分类与温度反演教程

本期带来监督分类教程&#xff0c;更多内容&#xff0c;欢迎关注小编的公众号梧桐凉月哦&#xff01;&#xff01;&#xff01; 一、研究区自然、地理环境特征&#xff1a; 1、景德镇市位于中国江西省东北部&#xff0c;地处赣江中游的赣北盆地&#xff0c;地形地貌以丘陵和低…

mitmproxy代理

文章目录 mitmproxy1. 网络代理2. 安装3. Https请求3.1 启动mitmproxy3.2 获取证书3.3 配置代理3.4 运行测试 4. 请求4.1 读取请求4.2 修改请求4.3 拦截请求 5. 响应5.1 读取响应5.2 修改响应 6. 案例&#xff1a;共享账号6.1 登录bilibili获取cookies6.2 在代理请求中设置cook…

ER-NeRF实时对话数字人模型训练与部署

ER-NeRF是基于NeRF用于生成数字人的方法&#xff0c;可以达到实时生成的效果。 下载源码 cd D:\Projects\ git clone https://github.com/Fictionarry/ER-NeRF cd D:\Projects\ER-NeRF 下载模型 准备面部解析模型 wget https://github.com/YudongGuo/AD-NeRF/blob/master/…

MyBatisPlus入门教程

MyBatisPlus MyBatis-Plus (opens new window)&#xff08;简称 MP&#xff09;是一个 MyBatis (opens new window) 的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 官网地址&#xff1a;https://baomidou.com/ 一、入门案…

sql注入之sqli-labs-less-1 错误注入

输入?id1 得到登录页面&#xff1a; 通过order by 函数试探&#xff1a; 5的时候报错 试探到3 的时候返回正确的值&#xff1a; 然后继续注入&#xff1a;?id -1 union select 1,2,3 -- 查看回显点&#xff1a; 开始查看数据库内容&#xff1a;id-1 union select 1,databa…

open-spider开源爬虫工具:抖音数据采集

在当今信息爆炸的时代&#xff0c;网络爬虫作为一种自动化的数据收集工具&#xff0c;其重要性不言而喻。它能够帮助我们从互联网上高效地提取和处理数据&#xff0c;为数据分析、市场研究、内容监控等领域提供支持。抖音作为一个全球性的短视频平台&#xff0c;拥有海量的用户…

CKA考生注意:这些Deployment要点能助你一臂之力!

往期精彩文章 : 提升CKA考试胜算&#xff1a;一文带你全面了解RBAC权限控制&#xff01;揭秘高效运维&#xff1a;如何用kubectl top命令实时监控K8s资源使用情况&#xff1f;CKA认证必备&#xff1a;掌握k8s网络策略的关键要点提高CKA认证成功率&#xff0c;CKA真题中的节点维…

68-解构赋值,迭代器,生成器函数

1.解构赋值(针对数组array&#xff0c;字符串String及对象object以) 结构赋值是一种特殊的语法&#xff0c;通过将各种结构中的元素复制到变量中达到"解构"的目的&#xff0c;但是数组本身没有改变 1.1解构单层数组 <script>let arr [1,2,3,4,5];//获取数组…

c++ primer学习笔记(一)

目录 第一章、c快速入门 重点&#xff1a;类的简介 第二章 1、基本内置类型 2、字面值常量 1、整型字面值规则 2、浮点字面值规则 3、布尔字面值 4、字符字面值 5、非打印字符的转义序列 ​编辑 6、字符串字面值 3、变量 1、变量标识符 2、定义和初始化对象 3、…

java: 无法访问org.springframework.web.bind.annotation.RequestMapping......类文件具有错误的版本 61.0, 应为 52.0

文章目录 一、报错问题二、问题背景三、原因分析四、解决方案 一、报错问题 java: 无法访问org.springframework.web.bind.annotation.RequestMapping 错误的类文件: /D:/SoftwareInstall/Maven/repository/org/springframework/spring-web/6.0.9/spring-web-6.0.9.jar!/org/s…

latex报错Repeated entry解决办法

报错原因——重复了两个参考文献&#xff0c;删掉一个即可 总结 "Repeated entry"这个错误通常出现在你尝试在LaTeX中多次使用同一个标签&#xff08;label&#xff09;或者多次插入相同的图像/表格等时。例如&#xff0c;在LaTeX中&#xff0c;我们可能会为每一个章…

Modern C++ std::any为何要求Tp可拷贝构造?

小问题也会影响设计的思路&#xff0c;某个问题或某种case的探讨有助于理解设计的初衷。 声明&#xff1a;以下_Tp/Tp都是指要放入std::any的对象的类型。 它要求_Tp is_copy_constructible, 仅仅是因为有很多函数的实现调用了Tp的拷贝构造函数吗&#xff1f;比如说上节提到的初…

动态SQL的处理

学习视频&#xff1a;3001 动态SQL中的元素_哔哩哔哩_bilibili 目录 1.1为什么学 1.2动态SQL中的元素 条件查询操作 if 元素 choose、when、otherwise元素 where、trim元素 更新操作 set元素使用场景 复杂查询操作 foreach 元素中的属性 ​编辑 迭代数组 迭代List 迭代Map 1…

第3部分 原理篇2去中心化数字身份标识符(DID)(4)

3.2.3. DID解析 3.2.3.1. DID解析参与方 图3-5 DID 解析过程 本聪老师&#xff1a;我们之前提到过&#xff0c;DID 解析过程是将 DID 转换为对应的 DID 文档。这样做的目的是验证 DID 所代表的主体的身份。那么解析过程会涉及哪些概念呢&#xff1f;我们看图3-&#xff0c;DI…

端智能:面向手机计算环境的端云协同AI技术创新

近年来&#xff0c;随着移动端设备软硬件能力的进步&#xff0c;移动端的算力有了很大提升&#xff0c;同时面向移动端的机器学习框架和模型轻量化技术越来越成熟&#xff0c;端上的AI能力逐渐进入大众视野&#xff0c;端智能在电商领域也开始逐步走向规模化应用。通过持续探索…