网站收录下降原因,做网站要准备什么资料,俄罗斯最新军事动态,山东网站建设网络公司文章目录vector的介绍vector的使用及其实现vector的定义vector iterator 的使用vector空间增长问题vector的增删查改vector的介绍 vector是表示可变大小数组的序列容器。就像数组一样#xff0c;vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素… 文章目录vector的介绍vector的使用及其实现vector的定义vector iterator 的使用vector空间增长问题vector的增删查改 vector的介绍 vector是表示可变大小数组的序列容器。就像数组一样vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问和数组一样高效。但是又不像数组它的大小是可以动态改变的而且它的大小会被容器自动处理。本质讲vector使用动态分配数组来存储它的元素。当新元素插入时候这个数组需要被重新分配大小为了增加存储空间。其做法是分配一个新的数组然后将全部元素移到这个数组。就时间而言这是一个相对代价高的任务因为每当一个新的元素加入到容器的时候vector并不会每次都重新分配大小。vector分配空间策略vector会分配一些额外的空间以适应可能的增长因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何重新分配都应该是对数增长的间隔大小以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。因此vector占用了更多的存储空间为了获得管理存储空间的能力并且以一种有效的方式动态增长。与其它动态序列容器相比deque, list and forward_list vector在访问元素的时候更加高效在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作效率更低。比起list和forward_list统一的迭代器和引用更好 vector的使用及其实现
vector的定义
(constructor)构造函数声明接口说明vector()重点无参构造vectorsize_type n, const value_type val value_type()构造并初始化n个valvector (const vector x); 重点拷贝构造vector (InputIterator first, InputIterator last);使用迭代器进行初始化构造
int main()
{vectorint v1;//无参构造vectorint v2(5, 3);for (auto ch : v2){cout ch ;}cout endl;vectorint v3(v2);//拷贝构造是定义一个新的赋值是已经定义好了一个了for (auto ch : v3){cout ch ;}cout endl;vectorint v4(v3.begin() 1, v3.end()- 1);for (auto ch : v4){cout ch ;}cout endl;return 0;
}模拟实现需要注意的地方注释在代码中了 vector():_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){}vector(size_t n,const T valT()):_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){reserve(n);for (int i 0; i n; i){push_back(val);}}vector(int n, const T val T()):_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){reserve(n);for (int i 0; i n; i){push_back(val);}}//1.vector(size_t n,const T valT())//2.vectorint v1(10, 5);//3.template class InputIterator//vector(InputIterator first, InputIterator last)//上面的三行注释的代码1和2互相冲突2会优先访问3它们的类型更加匹配//编译器会优先找与自己更加合适的人匹配//错误非法的间接寻址//解决办法可以在重载一个构造函数将size_t改成inttemplate class InputIteratorvector(InputIterator first, InputIterator last){while (first ! last){push_back(*first);first;}}//vector(const vectorT v)//{// _start new T[v.capacity()];// //memcpy(_start, v._start, sizeof(T)*v.size());// //因为memcpy也是进行浅拷贝vectorstring// for (size_t i 0; i v.size(); i)// {// _start[i] v._start[i];// //// }// _finish _start v.size();// _end_of_storage _start v.capacity();//}//这里使用上面被注释的代码拷贝构造也是可以的上面的更加规范//注意memcpy是不能随便用的会造成浅拷贝vector(const vectorT v){vectorT tmp(v.begin(), v.end());swap(tmp);}void swap(vectorT v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}//注意下面是传值临时拷贝所以交换时将他修改也是无所谓的//也并不会影响v2的结果//v1v2vectorT operator(vectorT v){swap(v);return *this;}从上面的两张图片当中在实现拷贝构造和赋值重载时是可以将vectorT替换为vector 在这里介绍一个自己当时的疑问为什么模板函数不能直接代替vectorint vector(const vectorT v) vector(int n, const T val T()) 问题也是从上面的两个函数参数得来的在第二个当中就能代替vectorint而上面的却不能。 T是vector存储的数据类型拷贝构造是要用已有的vector对象构造新的vector所以类型应该是const vectorT的 vector iterator 的使用
iterator的使用接口说明begin end重点获取第一个数据位置的iterator/const_iterator 获取最后一个数据的下一个位置的iterator/const_iteratorrbegin rend获取最后一个数据位置的reverse_iterator获取第一个数据前一个位置的reverse_iteratorint main()
{vectorint v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);vectorint::iterator it v1.begin();while (it ! v1.end()){cout *it ;it;}cout endl;vectorint::reverse_iterator rit v1.rbegin();while (rit !v1.rend()){cout *rit ;rit;}cout endl;
}相关模拟实现反向迭代器会在后期实现的。 typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}vector空间增长问题
容量空间接口说明size获取数据个数capacity获取容量大小empty判断是否为空resize重点改变vector的sizereserve 重点改变vector的capacitycapacity的代码在vs和g下分别运行会发现vs下capacity是按1.5倍增长的g是按2倍增长的。 这个问题经常会考察不要固化的认为vector增容都是2倍具体增长多少是根据具体的需求定义 的。vs是PJ版本STLg是SGI版本STL。reserve只负责开辟空间如果确定知道需要用多少空间reserve可以缓解vector增容的代价缺陷问 题。resize在开空间的同时还会进行初始化影响size。 // 测试vector的默认扩容机制
void TestVectorExpand()
{size_t sz;vectorint v;sz v.capacity();cout making v grow:\n;for (int i 0; i 100; i){v.push_back(i);if (sz ! v.capacity()){sz v.capacity();cout capacity changed: sz \n;}}
}
vs运行结果vs下使用的STL基本是按照1.5倍方式扩容
making foo grow :
capacity changed : 1
capacity changed : 2
capacity changed : 3
capacity changed : 4
capacity changed : 6
capacity changed : 9
capacity changed : 13
capacity changed : 19
capacity changed : 28
capacity changed : 42
capacity changed : 63
capacity changed : 94
capacity changed : 141
g运行结果linux下使用的STL基本是按照2倍方式扩容
making foo grow :
capacity changed : 1
capacity changed : 2
capacity changed : 4
capacity changed : 8
capacity changed : 16
capacity changed : 32
capacity changed : 64
capacity changed : 128
// 如果已经确定vector中要存储元素大概个数可以提前将空间设置足够
// 就可以避免边插入边扩容导致效率低下的问题了
void TestVectorExpandOP()
{vectorint v;size_t sz v.capacity();v.reserve(100); // 提前将容量设置好可以避免一遍插入一遍扩容cout making bar grow:\n;for (int i 0; i 100; i){v.push_back(i);if (sz ! v.capacity()){sz v.capacity();cout capacity changed: sz \n;}}
}下面为相关操作 int main()
{vectorint v1(5, 3);cout v1.size() endl;cout v1.capacity() endl;for (auto ch : v1){cout ch ;}cout endl;v1.reserve(10);cout v1.size() endl;cout v1.capacity() endl;for (auto ch : v1){cout ch ;}cout endl;v1.resize(20,1);cout v1.size() endl;cout v1.capacity() endl;for (auto ch : v1){cout ch ;}cout endl;return 0;
}模拟实现 bool empty() const{return _start _finish;}size_t size() const{return _finish - _start;}size_t capacity() const{return _end_of_storage - _start;}void reserve(size_t n){if (n capacity()){T* tmp new T[n];size_t sz size();//提前做好记录防止更改地址if (_start)//判断一下如果为空就可以省去这步{//memcpy(tmp, _start, sizeof(T) * n);//在扩容这里也是有问题的因为需要将数据移过来//如果只是进行浅拷贝在执行delete[] _start;时数据会销毁for (size_t i 0; i sz; i){tmp[i] _start[i];}delete[] _start;}//开新的空间地址会更改_strat,_finsh,_end_of_storage都会改变***_start tmp;_finish _start sz;_end_of_storage _start n;}}void resize(size_t n, T val T()){//这里的第二个参数是可以为任意类型if (n size()){_finish _start n;}else{if (n capacity()){reserve(n);}while (_finish ! _start n){*_finish val;_finish;}}}vector的增删查改
vector增删查改接口说明push_back重点尾插pop_back 重点尾删find查找。注意这个是算法模块实现不是vector的成员接口insert在position之前插入valerase删除position位置的数据swap交换两个vector的数据空间operator[] 重点像数组一样访问1.2.4 vector 迭代器失效问题。重点 迭代器的主要作用就是让算法能够不用关心底层数据结构其底层实际就是一个指针或者是对指针进行了 封装比如vector的迭代器就是原生态指针T* 。因此迭代器失效实际就是迭代器底层对应指针所指向的 空间被销毁了而使用一块已经被释放的空间造成的后果是程序崩溃(即如果继续使用已经失效的迭代器 程序可能会崩溃)。 对于vector可能会导致其迭代器失效的操作有 会引起其底层空间改变的操作都有可能是迭代器失效比如resize、reserve、insert、assign、 push_back等。 #include iostream
using namespace std;
#include vector
int main()
{vectorint v{ 1,2,3,4,5,6 };auto it v.begin();// 将有效元素个数增加到100个多出的位置使用8填充操作期间底层会扩容// v.resize(100, 8);// reserve的作用就是改变扩容大小但不改变有效元素个数操作期间可能会引起底层容量改变// v.reserve(100);// 插入元素期间可能会引起扩容而导致原空间被释放// v.insert(v.begin(), 0);// v.push_back(8);// 给vector重新赋值可能会引起底层容量改变v.assign(100, 8);/*出错原因以上操作都有可能会导致vector扩容也就是说vector底层原理旧空间被释放掉而在打印时it还使用的是释放之间的旧空间在对it迭代器操作时实际操作的是一块已经被释放的空间而引起代码运行时崩溃。解决方式在以上操作完成之后如果想要继续通过迭代器操作vector中的元素只需给it重新赋值即可。*/while (it ! v.end()){cout *it ;it;}cout endl;return 0;
}指定位置元素的删除操作–erase #include iostream
using namespace std;
#include vector
int main()
{int a[] { 1, 2, 3, 4 };vectorint v(a, a sizeof(a) / sizeof(int));// 使用find查找3所在位置的iteratorvectorint::iterator pos find(v.begin(), v.end(), 3);// 删除pos位置的数据导致pos迭代器失效。v.erase(pos);cout *pos endl; // 此处会导致非法访问return 0;
}erase删除pos位置元素后pos位置之后的元素会往前搬移没有导致底层空间的改变理论上讲迭代 器不应该会失效但是如果pos刚好是最后一个元素删完之后pos刚好是end的位置而end位置是 没有元素的那么pos就失效了。因此删除vector中任意位置上元素时vs就认为该位置迭代器失效 了。 以下代码的功能是删除vector中所有的偶数请问那个代码是正确的为什么 int main()
{vectorint v{ 1, 2, 3, 4 };auto it v.begin();while (it ! v.end()){if (*it % 2 0)v.erase(it);it;}return 0;
}
//这段代码的问题出在了当删除了最后一个值end和it正好错过
//因此it ! v.end()也就无法达到
//下面的代码是解决方法这也是erase为什么要有返回值的原因
int main()
{vectorint v{ 1, 2, 3, 4 };auto it v.begin();while (it ! v.end()){if (*it % 2 0)it v.erase(it);elseit;}return 0;
}迭代器失效解决办法在使用前对迭代器重新赋值即可 模拟实现 在这里要重点注意一下memcpy memcpy是内存的二进制格式拷贝将一段内存空间中内容原封不动的拷贝到另外一段内存空间中 如果拷贝的是自定义类型的元素memcpy既高效又不会出错但如果拷贝的是自定义类型元素并且 自定义类型元素中涉及到资源管理时就会出错因为memcpy的拷贝实际是浅拷贝。 void push_back(const T x){if (_finish _end_of_storage){reserve(capacity() 0 ? 4 : capacity() * 2);}*_finish x;_finish;}void pop_back(){assert(!empty());_finish--;}iterator insert(iterator pos, const T val){assert(pos _finish);assert(pos _start);if (_finish _end_of_storage){size_t len pos - _start;//防止迭代器失效reserve(capacity() 0 ? 4 : capacity() * 2);pos _start len;//迭代器失效}iterator end _finish - 1;while (end pos){*(end 1) *end;end--;}*pos val;_finish;return pos;}iterator erase(iterator pos){assert(pos _finish);assert(pos _start);iterator start pos 1;while (start ! _finish){*(start - 1) *(start);start;}_finish--;return pos;}T operator[](size_t n){assert(n size());return _start[n];}const T operator[](size_t n) const{assert(n size());return _start[n];}最后文章有什么不对的地方或者有什么更好的写法欢迎大家在评论区指出