当前位置:网站首页>STL——vector
STL——vector
2022-08-03 13:27:00 【原来45】
目录
作者和朋友建立的社区:非科班转码社区-CSDN社区云
期待hxd的支持哈
最后是打鸡血环节:你只管努力,剩下的交给天意
1. vector的介绍
vector - C++ Reference (cplusplus.com)
vector与string 理论上应该是一样的,但其实操作上有些不同,没有string方便。而且string和c有相关联,所以后面要有'\0'
1. vector是表示可变大小数组的序列容器。2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小,为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数(O1)时间的复杂度完成的。5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。6. 与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好。
PS:
在C++里面因为模板存在的原因,所以内置类型也是有构造函数的,析构也有,这样才能更好支持模板
2. vector的各个函数
以下列出常见的
vector的定义
(constructor)构造函数声明 接口说明 vector()(重点) 无参构造 vector(size_type n, const value_type& val = value_type()) 构造并初始化n个val vector (const vector& x); (重点) 拷贝构造 vector (InputIterator fifirst, InputIterator last); 使用迭代器进行初始化构造vector iterator 的使用
iterator的使用 接口说明 begin +end(重点) 获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置的iterator/const_iterator rbegin + rend 获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iteratorvector 空间增长问题
容量空间 接口说明 size 获取数据个数 capacity 获取容量大小 empty 判断是否为空 resize(重点) 改变vector的size reserve (重点) 改变vector的capacityvs下capacity是按1.5倍增长的,g++是按2倍增长的。但是不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。resize在开空间的同时还会进行初始化,同时影响size。如果已经确定vector中要存储元素大概个数,可以提前将空间设置足够就可以避免边插入边扩容导致效率低下的问题了vector 增删查改
vector增删查改 接口说明 push_back(重点) 尾插 pop_back (重点) 尾删 find 查找。(注意这个是算法模块实现,不是vector的成员接口) insert 在position之前插入val erase 删除position位置的数据 swap 交换两个vector的数据空间 operator[] (重点) 像数组一样访问
3. vector 迭代器失效问题
迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器,程序可能会崩溃)。
对于vector可能会导致其迭代器失效的操作有:会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、push_back等。以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释放掉,而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的空间,而引起代码运行时崩溃。解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给it重新赋值即可。问题:1.2.
扩容的时候也会有迭代器失效的问题,因为pos是值传递,外面的it没有改变
3.
往前插入时,然后++指向同一个值,会死循环
解决:
综合上面有两种失效,一个是野指针,一个是指向的意义变了
指定位置元素的删除操作--erase删除pos位置的数据,导致pos迭代器失效。erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。1. erase的失效都是意义变了,或者不在访问数据的有效范围。2.一般不会使用缩容的方案,那么erase的失效,一般也不存在野指针的失效SGI STL中,迭代器失效后,代码并不一定会崩溃,但是运行结果肯定不对,如果it不在begin和end范围内,肯定会崩溃的。与vector类似,string在插入+扩容操作+erase之后,迭代器也会失效扩容之后,it指向之前旧空间已经被释放了,该迭代器就失效了后序打印时,再访问it指向的空间程序就会崩溃迭代器失效解决办法:在使用前,对迭代器重新赋值即可。PS:Linux下,g++编译器对迭代器失效的检测并不是非常严格,处理也没有vs下极端。SGI STL中,迭代器失效后,代码并不一定会崩溃,但是运行结果肯定不对,如果it不在begin和end范围内,肯定会崩溃的。问题:错误匹配的时候解决:多增加一个重载版本
4. vector模拟实现及注意
注意: 对于浅拷贝的问题要多加注意,如用memcpy
1. memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中。2. 如果拷贝的是自定义类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。#pragma once #include<iostream> #include<assert.h> using std::cin; using std::cout; using std::endl; template<class T=int> class my_vector { public: // Vector的迭代器是一个原生指针 typedef T* iterator; typedef const T* const_iterator; iterator begin() { return _start; } iterator end() { return _finish; } const_iterator cbegin()const //注意这里的两个const { //前面限制返回值不可被修改 return _start; //后面限制传入this const修饰来调用此次函数 } const_iterator cend() const { return _finish; } // construct and destroy //默认构造 my_vector() :_start(nullptr), _finish(nullptr), _endOfStorage(nullptr) {} //n个值构造 my_vector(int n, const T& value = T()) // value = T() 默认构造 内置类型也有 :_start(nullptr), _finish(nullptr), _endOfStorage(nullptr) { reserve(n); for (int i = 0; i < n; i++) { push_back(value); } } //迭代器区间构造 template<class InputIterator> my_vector(InputIterator first, InputIterator last) :_start(nullptr), _finish(nullptr), _endOfStorage(nullptr) { while (first != last) { push_back(*first); first++; } } //拷贝构造 my_vector(const my_vector<T>& v) :_start(nullptr), _finish(nullptr), _endOfStorage(nullptr) { const_iterator first = v.cbegin(); while (first != v.cend()) { push_back(v[first]); first++; } } my_vector<T>& operator= (my_vector<T> v) { swap(v); return *this; } ~my_vector() { if (_start) { delete _start; _start = _endOfStorage = _finish = nullptr; } } // capacity. size_t size() const { return _finish - _start; } size_t capacity() const { return _endOfStorage - _start; } void reserve(size_t n) { size_t sz = size(); if (n > capacity()) { //注意是新new了个对象 //避免浅拷贝的问题 T* tmp = new T[n]; if (_start) { for (size_t i = 0; i < size(); i++) { tmp[i] = _start[i]; } delete _start; } _start = tmp; } _finish = _start + sz; _endOfStorage = _start + n; } void resize(size_t n, const T& value = T()) { if (n > capacity()) { reserve(n); } if (n > size()) { //1 2 3 8 5 while (_finish != _start + n) { *_finish = value; _finish++; } } else { _finish = _start + n; } } ///access/// T& operator[](size_t pos) { assert(pos<size()); return *(_start + pos); //return _start[pos]; } const T& operator[](size_t pos)const { assert(pos < size()); return *(_start + pos); } ///modify/ void push_back(const T& x) { if (_finish == _endOfStorage) { size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2; reserve(newCapacity); } *_finish = x; ++_finish; } void pop_back() { assert(_finish > _start); _finish--; } void swap(my_vector<T>& v) { std::swap(_start, v._start); std::swap(_finish, v._finish); std::swap(_endOfStorage, v._endOfStorage); } iterator insert(iterator pos, const T& x) { assert(pos >= _start && pos <= _finish); //这样就不用判断扩容,反正这个值也会被覆盖 push_back(0); size_t end = size()+1; while (end != (pos-_start)) { _start[end] = _start[end-1]; end--; } _start[end] = x; return &_start[end]; } iterator erase(iterator pos) { assert(pos >= begin() && pos < end()); iterator end = pos + 1; while (end!=_finish) { *(end - 1) = *end; end++; } _finish--; return pos; } private: //1 2 3 (空间大小为4) iterator _start; // 指向数据块的开始 iterator _finish; // 指向有效数据的尾 end是\0 iterator _endOfStorage; // 指向存储容量的尾 }; // //int main() //{ // my_vector<int> v; // // return 0; //}
最后的最后,创作不易,希望读者三连支持
赠人玫瑰,手有余香
边栏推荐
- 8/2 训练日志(dp+思维+字典树)
- [Practical skills] APP video tutorial for updating APP in CANFD, I2C, SPI and serial port mode of single-chip bootloader (2022-08-01)
- 【web渗透】CSRF漏洞详细讲解
- 国产替代风潮下,电子元器件B2B商城系统如何助力企业突围市场竞争
- [Deep Learning] Overview of Efficient and Lightweight Semantic Segmentation
- An animation optimization of shape tween and optimization of traditional tweening
- CVPR 2022 | Predicting Skeletons from Human Meshes, True Physiological Skeletons!
- 为什么手动启动GBase 8c数据库中GTM节点
- 保健用品行业B2B电子商务系统:供采交易全链路数字化,助推企业管理精细化
- js \n\r 换行失败 :【white-space: pre-line;】${} Template Literals
猜你喜欢
Comics: how do you prove that sleep does not release the lock, and wait to release lock?
苹果终于认清现实,销量成为优先考虑,iPhone14将不涨价
力扣刷题 每日两题(一)
保健用品行业B2B电子商务系统:供采交易全链路数字化,助推企业管理精细化
软件测试面试(四)
[Microservice] Multi-level cache
The components of the basis of An animation movie clip animation between traditional filling
OpenCV perspective transform
TiFlash 计算层概览
PyTorch构建分类网络模型(Mnist数据集,全连接神经网络)
随机推荐
An introduction to the pen tool, pencil tool and brush tool
secureCRT连接开发板连接不上问题解决
汉源高科G8032标准ERPS环网交换机千兆4光10电工业以太网交换机环网+WEB管理+SNMP划VLAN
安全狗《云原生安全威胁分析报告》首次提出双检测模型
Basic principle of the bulk of the animation and shape the An animation tip point
Golang structs & methods
Yahoo!Answers - data set
爱可可AI前沿推介(8.3)
Golang sync.WaitGroup
PyTorch builds a neural network to predict temperature (dataset comparison, CPU vs GPU comparison)
一文详解什么是软件部署
国产替代风潮下,电子元器件B2B商城系统如何助力企业突围市场竞争
js单线程及事件循环、宏任务和微任务
第07章 InnoDB数据存储结构【2.索引及调优篇】【MySQL高级】
Golang channel channel
【OpenCV】 级联分类器训练模型
为什么手动启动GBase 8c数据库中GTM节点
Jmeter use
Golang 字典 map
驻冰岛使馆提醒旅冰中国公民务必加强安全防护