
1.vector 的使用下面借助vector的文档来介绍一下一下vector常用的接口。1.1成员函数主要的成员函数有构造函数析构函数赋值运算符重载.构造函数常用的接口说明// 调用无参构造创建空vectorsize为0没有元素 vectorint v1; // 调用n个val构造创建5个元素每个元素初始值为1 vectorint v2(5, 1); // 迭代器区间构造[v2.begin(), v2.end())把v2的全部元素拷贝构造v3 vectorint v3(v2.begin(), v2.end()); int a1[] { 1,2,3,4,5 }; // 原生指针充当迭代器[a1 , a15)将数组所有元素拷贝到v4 // sizeof(a1)/sizeof(a1[0])计算数组中元素总个数 vectorint v4(a1, a1 sizeof(a1) / sizeof(a1[0])); // 拷贝构造函数深拷贝v4v5和v4内存相互独立 vectorint v5(v4); // C11初始化列表构造函数花括号直接给出一组初始值 vectorint v6{ 2,3,3,4,5 };注意上面代码的v4对象的构造是因为数组名a1等价数组首元素指针指针也是迭代器原生指针满足 InputIterator 要求sizeof(a1)/sizeof(a1[0])计算数组元素个数 5a15指向数组末尾的下一个位置。析构函数和赋值重载函数就比较简单理解就不在介绍。1.2 vector iterator 的使用有了string类的使用基础对迭代器的使用理解就更简单了vectorintv{1, 2,3,4,5 }; vectorint::iterator it v.begin(); while (it ! v.end()) { cout (*it); it; } cout endl; vectorint::reverse_iterator rit v.rbegin(); while (rit ! v.rend()) { cout (*rit); rit; }1.3vector 空间增长问题reserve只负责开辟空间如果确定知道需要用多少空间reserve可以缓解vector增容的代价缺陷问题。resize在开空间的同时还会进行初始化影响size如果nsize(),就用val的值增加数据nsize()就删除数据到n个ncapacity(),就会扩容到n。vectorintv{ 1, 2,3,4,5 }; cout v.size() endl; v.resize(10, 1); cout v.size() endl; cout v.capacity() endl; v.reserve(20); cout v.capacity() endl; cout v.empty() endl;1.4元素访问接口的使用注意如果 vector 为空调用front()、back()程序行为未定义vectorintv{ 1, 2,3,4,5 }; cout v[2] endl; cout v.at(2) endl; cout v.front() endl; cout v.back() endl; cout (*v.data()) endl;1.5vector 的增删查改注意insert()和erase()会造成迭代器失效。vectorint v {1,2,3}; // assign清空原有内容赋值3个10 v.assign(3, 10); // v:{10,10,10} // push_back 尾插元素 v.push_back(20); // v:{10,10,10,20} // pop_back 尾删无返回值 v.pop_back(); // v:{10,10,10} // insert 在迭代器位置插入元素这里在begin位置插入5 v.insert(v.begin(),5); // v:{5,10,10,10} // erase 删除迭代器指向的元素返回下一个有效迭代器 v.erase(v.begin()); // v:{10,10,10} vectorint v2{100,200}; // swap交换两个vector底层资源效率很高 v.swap(v2); // clear清空所有元素size变为0capacity容量不变 v.clear();vector的重点使用主要就是这些下面来模拟实现一个vector,主要包含一些常用的接口为了更好的了解底层。2.vector的模拟实现模拟实现简易vector实现了部分接口底层维护三个指针_start:指向有效数据开始位置_finish:指向最后一个有效数据的下一个位置_endofstorage:指向空间的最有一个位置完整代码#pragma once #includevector #includeiostream #includestring #include initializer_list #includeassert.h namespace gxy { templateclass T class vector { public: 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() default; //拷贝构造 深拷贝 vector(const vectorT v) { reserve(v.capacity()); for (auto e : v) { push_back(e); } } //交换底层三个指针 void swap(vectorT v) { std::swap(_start, v._start); std::swap(_finish, v._finish); std::swap(_endofstorage, v._endofstorage); } //赋值重载 现代写法传值swap vectorT operator(vectorT v) { swap(v); return *this; } //迭代器区间构造模板支持任意输入迭代器 template class InputIterator vector(InputIterator first, InputIterator last) { while (first ! last) { push_back(*first); first; } } //C11 initializer_list初始化列表构造 vector(std::initializer_listT il) { reserve(il.size()); for (auto e : il) { push_back(e); } } //析构函数释放堆数组 ~vector() { if (_start) { delete[] _start; _start _finish _endofstorage nullptr; } } //有效元素个数 size_t size() const { return _finish - _start; } //总容量 size_t capacity() const { return _endofstorage - _start; } //reserve扩容只开空间不修改size void reserve(size_t n) { if (n capacity()) { size_t oldsize size(); T* tmp new T[n]; if (_start) { //不能memcpymemcpy浅拷贝如果T是string等自定义类型会出问题 for (size_t i 0;i size();i) { tmp[i] _start[i]; } delete[] _start; } _start tmp; _finish _start oldsize; _endofstorage _start n; } } //resize 修改size可以扩容、填充、截断 void resize(size_t n, T val T()) { if (n size()) { reserve(n); while (_finish ! _start n) { *_finish val; _finish; } } else { _finish _start n; } } //下标访问 T operator[](size_t i) { assert(i size()); return _start[i]; } const T operator[] (size_t i) const { assert(i size()); return _start[i]; } //尾插 void push_back(const T val) { if (_finish _endofstorage) { size_t newcapacity capacity() 0 ? 4 : 2 * capacity(); reserve(newcapacity); } *_finish val; _finish; } bool empty() { return _start _finish; } //尾删 void pop_back() { assert(!empty()); _finish--; } //pos位置插入元素 iterator insert(iterator pos, const T x) { assert(pos _finish); assert(pos _start); //扩容会导致pos迭代器失效扩容后pos变成野指针 if (_endofstorage _finish) { size_t newcapacity capacity() 0 ? 4 : 2 * capacity(); reserve(newcapacity); } //后移元素 iterator end _finish; while (end ! pos) { *end *(end - 1); end--; } *pos x; _finish; return pos; } //删除pos位置元素 iterator erase(iterator pos) { assert(pos _start); assert(pos _finish); iterator it pos; while (it ! _finish) { *it *(it1); it; } _finish--; return pos; } private: iterator _startnullptr; //指向有效数据开始位置 iterator _finishnullptr; //指向最后一个有效数据的下一个位置 iterator _endofstoragenullptr; //指向空间的末尾下一个位置 }; }下面主要分析一下一些实现细节2.1三个迭代器指针理解vector底层一块连续堆内存靠三个指针维护size()_finish-_start指针相减得到元素个数;capacity(_endofstorage - _start总容量;finish和_endofstorage之间是已经开辟但是没有使用的空闲空间。2.2reserve为什么不能用memcpymemcpy是字节拷贝属于浅拷贝。如果存储的是string等管理资源的自定义类型memcpy只会拷贝指针值析构的时候会出现doublefree重复释放所以循环赋值调用operator做深拷贝。2.3拷贝构造和赋值重载现代写法拷贝构造调用reserve开辟同等容量循环push_back完成深拷贝。赋值重载传值传参调用拷贝构造出临时对象swap交换资源出函数作用域临时对象析构释放放资源代码简洁但是要注意成员变量初始化的问题。2.4insert和erase存在迭代器失效的问题insert扩容的时候旧空间被释放原来传入的pos迭代器指向已经释放掉的旧空间变成野指针。这里实现采用返回新插入位置的迭代器再使用时要注意接收防止出现迭代器实效的问题。erase删除元素之后后面元素向前挪动pos迭代器本身还指向有效位置原来pos位置存放原来pos1的元素但是pos1等迭代器全部失效。erase返回下一个有效迭代器。2.5初始化列表迭代区间构造模板函数templateclassInputIterator可以接收任意满足输入迭代器类型既可以是vector迭代器也可以是原生数组指针。std::initializer_list支持花括号{1,2,3}直接初始化。本文主要介绍我个人学习使用vector 的过程和模拟实现vecotr理解底层原理。