STL:vector的常用接口使用及底层讲解和实现
目录
1.vector的介绍及使用
1.1 vector的介绍
vector的文档介绍:https://cplusplus.com/reference/vector/vector/
1.2 vector的使用
1.2.1vector的构造
| 构造函数 | 功能 |
| vector()(重点) | 无参构造 |
| vector(size_type n, const value_type& val = value_type()) | 构造并初始化n个val |
| vector (const vector& x); (重点) | 拷贝构造 |
| vector (InputIterator first, InputIterator last); | 使用迭代器进行初始化构造(可以用于其他容器构造vector) |
1.2.2 vector iterator 的使用
| 接口 | 功能 |
| begin()+end() | begin()获取第一个数据位置的iterator/const_iterator, end()获取最后一个数据的下 一个位置的iterator/const_iterator |
| rbegin()+rend() | rbegin()获取最后一个数据位置的reverse_iterator,rend()获取第一个数据前一个位置 的reverse_iterator |


1.2.3vector的空间增长问题
1.size()函数,获取数据个数
函数原型:size_type size() const;
2.capacity()函数,获取容量大小
函数原型:size_type capacity() const;
3.empty()函数,判空,空返回true,否则返回false
函数原型:bool empty() const;
4.resize()函数,
函数原型:void resize (size_type n, value_type val = value_type());
如果n<size()会保留前n个元素删除其他多余的元素,
如果n>size()则会插入元素使size()达到n个,若指定val就会填充val
如果n>capacity()则会重新分配空间使size()=capacity()=n
5.reserve()函数
函数原型:void reserve (size_type n);
如果n<capactiy()什么都不变
如果n>capacity(),则会重新分配空间使capacity()=n。
注:capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2 倍增长的。这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体需求定义的。vs是PJ版本STL,g++是SGI版本STL。
reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。
resize在开空间的同时还会进行初始化,影响size。
// 测试vector的默认扩容机制 void TestVectorExpand() { size_t sz; vector<int> 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'; } } } 
1.2.3 vector 增删查改
1.push_back(),尾插
函数原型:void push_back (const value_type& val);
2.pop_back(),尾删
函数原型 void pop_back();
3.find()(注意这个是算法模块实现,不是vector的成员接口),查找
函数原型:InputIterator find (InputIterator first, InputIterator last, const T& val);
使用时使用迭代器传一个区间和要查找的值,区间左闭右开[first,last),若区间有要查找的值则返回值所在的迭代器,若没有则返回last
4.insert()插入
函数原型:iterator insert (iterator position, const value_type& val);void insert (iterator position, size_type n, const value_type& val);void insert (iterator position, InputIterator first, InputIterator last);
在position位置前插入val;
在position位置前插入n个val;
在position位置前插入一个区间的值,区间左闭右开;
insert()插入改变size()大小,同时如果插入数量多余capacity()剩余空间就会分配新空间这会导致迭代器失效,同时因为vector底层是数组所以插入需要每个数据后移代码效率较低
5.erase() 删除
函数原型: iterator erase (iterator position); iterator erase (iterator first, iterator last);
删除position位置的元素
删除一个区间的元素,区间左开右闭
erase改变size()大小,同时删除后的元素需要前移,迭代器失效同时效率较低
6.swap() 交换
函数原型:template <class T, class Alloc>void swap (vector<T,Alloc>& x, vector<T,Alloc>& y);
容器x的内容与y容器的内容物交换。两个容器对象必须具有相同类型(相同的模板参数),尽管大小可能不同。与算法中实现的swap效果相同
// swap (vector overload) #include <iostream> #include <vector> main () { unsigned int i; std::vector<int> foo (3,100); // three ints with a value of 100 std::vector<int> bar (5,200); // five ints with a value of 200 foo.swap(bar); std::cout << "foo contains:"; for (std::vector<int>::iterator it = foo.begin(); it!=foo.end(); ++it) std::cout << ' ' << *it; std::cout << '\n'; std::cout << "bar contains:"; for (std::vector<int>::iterator it = bar.begin(); it!=bar.end(); ++it) std::cout << ' ' << *it; std::cout << '\n'; return 0; }7.operator[]
函数原型: reference operator[] (size_type n); const_reference operator[] (size_type n) const;
返回向量容器中位置 n 的元素的引用。一个类似的成员函数vector::at与该算符函数行为相同,但vector::at是边界检查的,通过抛出out_of_range异常来表示请求位置超出范围。所以要注意n 不要超过边界
1.2.4vector 迭代器失效问题。(重点)
迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对 指针进行了封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器 底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即 如果继续使用已经失效的迭代器,程序可能会崩溃)
1.会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、 assign、push_back等。
#include <iostream> using namespace std; #include <vector> int main() { vector<int> 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; } 2. 指定位置元素的删除操作--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查找3所在位置的iterator vector<int>::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就认为该位置迭代器失效了。
3. 与vector类似,string在插入+扩容操作+erase之后,迭代器也会失效
#include <string> void TestString() { string s("hello"); auto it = s.begin(); // 放开之后代码会崩溃,因为resize到20会string会进行扩容 // 扩容之后,it指向之前旧空间已经被释放了,该迭代器就失效了 // 后序打印时,再访问it指向的空间程序就会崩溃 //s.resize(20, '!'); while (it != s.end()) { cout << *it; ++it; } cout << endl; it = s.begin(); while (it != s.end()) { it = s.erase(it); // 按照下面方式写,运行时程序会崩溃,因为erase(it)之后 // it位置的迭代器就失效了 // s.erase(it); ++it; } } 2.vector深度剖析及模拟实现

vector常用接口的代码实现
#pragma once #include<assert.h> #include <iostream> #include <algorithm> #include<vector> #include<list> #include<string> using namespace std; namespace lyp { template<class T> class vector { public: typedef T* iterator; typedef const T* const_iterator; vector() = default; vector(const vector<T>& v) { reserve(v.size()); for (int i = 0; i < size(); i++) { push_back(v[i]); } } vector<T>& operator=(vector<T> v) { swap(v); return *this; } void swap(vector<T>& v) { std::swap(_start, v._start); std::swap(_finish, v._finish); std::swap(_end_of, v._end_of); } /*template<class Iterator> vector(Iterator first, Iterator last) { while (first!=last) { push_back(*first); first++; } }*/ //迭代器区间构造 /*template<class inputIterator> vector(inputIterator first, inputIterator last) { while (first != last) { push_back(*first); first++; } }*/ vector(size_t n, const T& val = T()) { reserve(n); for (int i = 0; i < n; i++) { push_back(val); } } /*vector(int n, const T& val = T()) { reserve(n); for (int i = 0; i < n; i++) { push_back(val); } }*/ ~vector() { if (_start) { delete[] _start; _start = _finish = _end_of = nullptr; } } iterator begin() { return _start; } iterator end() { return _finish; } T& operator[](size_t n) { return *(_start + n); } const T& operator[](size_t i) const { assert(i < size()); return _start[i]; } bool empty() { return _start == _finish; } void reserve(size_t n) { if (n > capacity()) { size_t old_size = size(); T* tmp = new T[n]; for (int i = 0; i < size(); i++) { tmp[i] = _start[i]; } delete[] _start; _start = tmp; _finish = tmp + old_size; _end_of = tmp + n; } } void resize(size_t n,T val=T()) { if (n < size()) { _finish = _start + n; } else { //reserve(n); while (_finish < _start + n) { *_finish = val; ++_finish; /*push_back(val);*/ } } } void push_back(const T& val) { if (_finish == _end_of) { reserve(capacity() == 0 ? 4 : capacity() * 2); } *_finish = val; _finish++; } void pop_back() { assert(!empty()); _finish--; } iterator insert(iterator pos, const T& x) { assert(pos<_finish && pos>_start); if (_finish == _end_of) { size_t n = pos - _start; reserve(capacity() == 0 ? 4 : capacity * 2); pos = _start + n; } int i = 0; while (pos + i < _finish) { *(_finish - i - 1) = *(_finish - i); i++; } *pos = x; _finish++; return pos; } void erase(iterator pos) { assert(!empty()); assert(pos<_finish && pos>_start); int i = 0; while (pos + i < _finish) { *(pos + i) = *(pos + 1 + i); i++; } _finish--; } T* data() { return _finish; } int size()const { return _finish - _start; } int capacity() { return _end_of - _start; } private: T* _start = nullptr; T* _finish = nullptr; T* _end_of = nullptr; }; template<class T> void print_T(T& val) { for (auto& e : val) { cout << e << " "; } cout << endl; } void test_vector1() { vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); for (size_t i = 0; i < v.size(); i++) { cout << v[i] << " "; } cout << endl; vector<int>::iterator it = v.begin(); while (it != v.end()) { cout << *it << " "; ++it; } cout << endl; for (auto e : v) { cout << e << " "; } cout << endl; print_T(v); vector<double> vd; vd.push_back(1.1); vd.push_back(2.1); vd.push_back(3.1); vd.push_back(4.1); vd.push_back(5.1); print_T(vd); } void test_vector2() { std::vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); print_T(v); /*v.insert(v.begin() + 2, 30); print_vector(v);*/ int x; cin >> x; auto p = find(v.begin(), v.end(), x); if (p != v.end()) { // insert以后p就是失效,不要直接访问,要访问就要更新这个失效的迭代器的值 /*v.insert(p, 20); (*p) *= 10;*/ p = v.insert(p, 40); (*(p + 1)) *= 10; } print_T(v); } void test_vector3() { std::vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); print_T(v); // 删除所有的偶数 auto it = v.begin(); while (it != v.end()) { if (*it % 2 == 0) { it = v.erase(it); } else { ++it; } } print_T(v); v.reserve(20); print_T(v); } void test_vector4() { int i = int(); int j = int(1); int k(2); vector<int> v; v.resize(10, 1); print_T(v); v.reserve(20); print_T(v); cout << v.size() << endl; cout << v.capacity() << endl; v.resize(15, 2); print_T(v); v.resize(25, 3); print_T(v); v.resize(5); print_T(v); } void test_vector5() { vector<int> v1; v1.push_back(1); v1.push_back(2); v1.push_back(3); v1.push_back(4); print_T(v1); vector<int> v2 = v1; print_T(v2); vector<int> v3; v3.push_back(10); v3.push_back(20); v3.push_back(30); v1 = v3; print_T(v1); print_T(v3); } void test_vector6() { vector<int> v1; v1.push_back(1); v1.push_back(2); v1.push_back(3); v1.push_back(4); v1.push_back(4); v1.push_back(4); /*vector<int> v2(v1.begin(), v1.begin() + 3); print_T(v1); print_T(v2);*/ list<int> lt; lt.push_back(10); lt.push_back(10); lt.push_back(10); lt.push_back(10); /*vector<int> v3(lt.begin(), lt.end());*/ print_T(lt); /*print_T(v2);*/ vector<string> v4(10, "1111111"); print_T(v4); vector<int> v5(10); print_T(v5); vector<int> v6(10u, 1); print_T(v6); vector<int> v7(10, 1); print_T(v7); } void test_vector7() { vector<string> v; v.push_back("11111111111111111111"); v.push_back("11111111111111111111"); v.push_back("11111111111111111111"); v.push_back("11111111111111111111"); print_T(v); v.push_back("11111111111111111111"); print_T(v); } } 2.2 动态二维数组理解
// 以杨慧三角的前n行为例:假设n为5 void test2vector(size_t n) { // 使用vector定义二维数组vv,vv中的每个元素都是vector<int> bit::vector<bit::vector<int>> vv(n); // 将二维数组每一行中的vecotr<int>中的元素全部设置为1 for (size_t i = 0; i < n; ++i) vv[i].resize(i + 1, 1); // 给杨慧三角出第一列和对角线的所有元素赋值 for (int i = 2; i < n; ++i) { for (int j = 1; j < i; ++j) { vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1]; } } }bit::vector> vv(n); 构造一个vv动态二维数组,vv中总共有n个元素,每个元素 都是vector类型的,每行没有包含任何元素,如果n为5时如下所示:
