文章目录
- vector的介绍及使用
- 介绍
- 使用
- 构造函数
- vector() :无参数构造
- [vector(size_type n, const value_type& val =value_type())](https://legacy.cplusplus.com/reference/vector/vector/vector/):构造并初始化n个val
- [vector (const vector& x)](https://legacy.cplusplus.com/reference/vector/vector/vector/) : 拷贝构造
- [vector (InputIterator first, InputIterator last)](https://legacy.cplusplus.com/reference/vector/vector/vector/) :使用迭代器进行初始化构造
- iterator的使用
- begin](https://legacy.cplusplus.com/reference/vector/vector/begin/)+[end
- rbegin](https://legacy.cplusplus.com/reference/vector/vector/rbegin/)+[rend
- vector的空间增长
- 函数基础使用
- reserve缓解增容代价
- reszie 开空间的同时初始化、影响size
- 增删查改
- vector 迭代器失效问题
- 插入操作导致迭代器失效
- 删除操作导致的迭代器失效
- `resize` 和 `reserve` 操作导致的迭代器失效
- 避免迭代器失效的方法
- vector的模拟实现
vector的介绍及使用
介绍
在 C++ 中,vector
是标准模板库(STL)提供的动态数组容器,它可以存储同类型元素,并且支持动态大小调整,是日常开发中非常常用的数据结构。
使用
构造函数
构造函数 | 接口说明 |
---|
vector() | 无参数构造 |
vector(size_type n, const value_type& val =value_type()) | 构造并初始化n个val |
vector (const vector& x); | 拷贝构造 |
vector (InputIterator first, InputIterator last); | 使用迭代器进行初始化构造 |
#include <iostream>
#include <vector>
using namespace std;int main() {vector<int> v1;cout << "v1 的大小: " << v1.size() << endl;cout << "v1 是否为空: " << (v1.empty() ? "是" : "否") << endl;return 0;
}

#include <iostream>
#include <vector>
using namespace std;int main() {vector<int> v2(5, 10);cout << "v2 的元素: ";for (int num : v2) {cout << num << " ";}cout << endl;return 0;
}

#include <iostream>
#include <vector>
using namespace std;int main() {vector<int> v3 = {1, 2, 3, 4, 5};vector<int> v4(v3);cout << "v4 的元素: ";for (int num : v4) {cout << num << " ";}cout << endl;return 0;
}

#include <iostream>
#include <vector>
#include <list>
using namespace std;int main() {list<int> l = {6, 7, 8, 9, 10};vector<int> v5(l.begin(), l.end());cout << "v5 的元素: ";for (int num : v5) {cout << num << " ";}cout << endl;return 0;
}

iterator的使用
iterator的使用 | 接口说明 |
---|
begin+end | 获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置的iterator/const_iterator |
rbegin+rend | 获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置 |

#include <iostream>
#include <vector>
using namespace std;int main() {vector<int> v = {1, 2, 3, 4, 5};cout << "正向遍历:";for (vector<int>::iterator it = v.begin(); it != v.end(); ++it) {cout << *it << " ";}cout << endl;return 0;
}

#include <iostream>
#include <vector>
using namespace std;int main() {vector<int> v = {1, 2, 3, 4, 5};cout << "反向遍历:";for (vector<int>::reverse_iterator rit = v.rbegin(); rit != v.rend(); ++rit) {cout << *rit << " ";}cout << endl;return 0;
}

vector的空间增长
容量空间 | 接口说明 |
---|
size | 获取数据个数 |
capacity | 获取容量大小 |
empty | 判断是否为空 |
resize | 改变vector的size |
reserve | 改变vector的capacity |
- capacity的代码在vs和g++下分别运行会发现vs下capacity是按1.5倍增长的,g++是按2倍增长的。所以不同编译器容量增长策略可能不同,实际以编译器为准)
- reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题
- resize在开空间的同时还会进行初始化,影响size。
函数基础使用
#include <iostream>
#include <vector>
using namespace std;int main() {vector<int> v = {1, 2, 3, 4, 5};cout << "size: " << v.size() << endl;cout << "capacity: " << v.capacity() << endl;cout << "empty: " << (v.empty() ? "是" : "否") << endl;v.resize(8, 10); cout << "resize 后 size: " << v.size() << endl;cout << "resize 后元素: ";for (int num : v) {cout << num << " ";}cout << endl;v.reserve(10);cout << "reserve 后 capacity: " << v.capacity() << endl;return 0;
}

reserve缓解增容代价
#include <iostream>
#include <vector>
#include <chrono>
using namespace std;
using namespace chrono;int main() {vector<int> v1;auto start1 = high_resolution_clock::now(); for (int i = 0; i < 1000000; ++i) { v1.push_back(i);}auto end1 = high_resolution_clock::now(); cout << "不使用 reserve 耗时: " << duration_cast<microseconds>(end1 - start1).count() << " 微秒" << endl;vector<int> v2;v2.reserve(1000000); auto start2 = high_resolution_clock::now();for (int i = 0; i < 1000000; ++i) {v2.push_back(i);}auto end2 = high_resolution_clock::now();cout << "使用 reserve 耗时: " << duration_cast<microseconds>(end2 - start2).count() << " 微秒" << endl;return 0;
}

reszie 开空间的同时初始化、影响size
#include <iostream>
#include <vector>
using namespace std;int main() {vector<int> v;v.resize(5, 10);cout << "resize 后 size: " << v.size() << endl;cout << "resize 后元素: ";for (int num : v) {cout << num << " ";}cout << endl;return 0;
}

增删查改
增删查改 | 说明 |
---|
push_back | 尾插 |
pop_back | 尾删 |
insert | 在position之前插入val |
erase | 删除position位置的数据 |
swap | 交换两个vector的数据空间 |
operator[] | 像数组一样访问 |
#include <iostream>
#include <vector>
using namespace std;int main() {vector<int> v = {1, 2, 3, 4, 5};v.push_back(6);cout << "push_back 后元素:";for (int num : v) {cout << num << " ";}cout << endl;v.pop_back();cout << "pop_back 后元素:";for (int num : v) {cout << num << " ";}cout << endl;v.insert(v.begin() + 2, 10);cout << "insert 后元素:";for (int num : v) {cout << num << " ";}cout << endl;v.erase(v.begin() + 3);cout << "erase 后元素:";for (int num : v) {cout << num << " ";}cout << endl;vector<int> v2 = {100, 200, 300};v.swap(v2);cout << "v 交换后元素:";for (int num : v) {cout << num << " ";}cout << endl;cout << "v2 交换后元素:";for (int num : v2) {cout << num << " ";}cout << endl;cout << "v[1] 的值:" << v[1] << endl;return 0;
}

vector 迭代器失效问题
迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对 指针进行了封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器 底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即 如果继续使用已经失效的迭代器,程序可能会崩溃)。
插入操作导致迭代器失效
push_back
与 emplace_back
- 当
vector
的容量(capacity
)足够时,在尾部插入元素不会导致迭代器失效,因为元素插入在末尾其他元素的位置不变。 - 但当
vector
容量不足,需要重新分配内存时,所有指向vector
的迭代器都会失效。这是因为重新分配内存后,vector
中元素的存储地址发生了改变。
#include <iostream>
#include <vector>
using namespace std;int main() {vector<int> v = {1, 2, 3};vector<int>::iterator it = v.begin();v.push_back(4); cout << *it << endl; v.push_back(5); return 0;
}
insert
:在vector
的任意位置插入元素,如果插入操作导致内存重新分配,所有迭代器都会失效。若没有重新分配内存,插入点之后的迭代器会失效,因为插入元素后,插入点之后的元素位置都发生了改变
#include <iostream>
#include <vector>
using namespace std;int main() {vector<int> v = {1, 2, 3};vector<int>::iterator it = v.begin() + 1;v.insert(v.begin(), 0); return 0;
}
删除操作导致的迭代器失效
pop_back
:删除vector
的尾部元素,如果删除后vector
的容量没有改变,除了指向被删除元素的迭代器外,其他迭代器仍然有效。但如果删除后vector
的容量发生变化(比如释放了部分内存),则指向vector
的迭代器可能会失效。
#include <iostream>
#include <vector>
using namespace std;int main() {vector<int> v = {1, 2, 3};vector<int>::iterator it = v.begin() + 1;v.pop_back(); cout << *it << endl; return 0;
}
erase
:删除指定位置的元素,指向被删除元素以及其后的迭代器都会失效,因为被删除元素之后的元素会向前移动。
#include <iostream>
#include <vector>
using namespace std;int main() {vector<int> v = {1, 2, 3};vector<int>::iterator it = v.begin() + 1;v.erase(v.begin()); return 0;
}
resize
和 reserve
操作导致的迭代器失效
resize
:调整vector
的大小,如果新大小比原来小,超出新大小部分的迭代器会失效;如果新大小比原来大且需要重新分配内存,所有迭代器都会失效;如果新大小比原来大但不需要重新分配内存,插入新元素位置之后的迭代器会失效。reserve
:如果reserve
的参数大于当前capacity
,会导致内存重新分配,所有迭代器都会失效;如果reserve
的参数小于等于当前capacity
,迭代器不会失效。
避免迭代器失效的方法
- 捕获插入或删除操作返回的迭代器:
insert
和erase
操作会返回一个新的迭代器,指向插入或删除操作之后的位置,通过更新迭代器,可以确保迭代器的有效性。 - 在循环中使用迭代器时特别注意:在对
vector
进行插入或删除操作的循环中,每次操作后及时更新迭代器,以避免迭代器失效带来的问题。
vector的模拟实现
模拟实现
#pragma once
#include <iostream>
#include <assert.h>
#include <cstring>
using namespace std;namespace myvector
{template <class T>class vector{public:typedef T *iterator;typedef const T *const_iterator;vector() {}size_t size() const{return _finish - _start;}size_t capacity() const{return _endofstorage - _start;}void reserve(size_t n){if (n > capacity()){size_t oldSize = size();T *tmp = new T[n]; if (_start){memcpy(tmp, _start, sizeof(T) * oldSize); delete[] _start; }_start = tmp;_finish = _start + oldSize;_endofstorage = _start + n;}}iterator insert(iterator pos, const T &x){assert(pos >= _start && pos <= _finish);if (_finish == _endofstorage){size_t len = pos - _start; reserve(capacity() == 0 ? 4 : capacity() * 2); pos = _start + len; }iterator i = _finish - 1;while (i >= pos){*(i + 1) = *i;--i;}*pos = x;++_finish;return pos;}void push_back(const T &x){insert(_finish, x);}vector(initializer_list<T> il): _start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(il.size());for (auto &e : il){push_back(e);}}~vector(){if (_start){delete[] _start;_start = _finish = _endofstorage = nullptr;}}bool empty(){return _start == _finish;}void pop_back(){assert(!empty());--_finish;}iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}T &operator[](size_t i){assert(i < size()); return _start[i];}iterator erase(iterator pos){assert(pos >= _start);assert(pos >= _finish);iterator i = pos + 1;while (i < _finish){*(i - 1) = *i;++i;}--_finish;return pos;}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 (size_t 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(const vector<T>& v){reserve(v.capacity());for (auto& e : v){push_back(e);}}void swap(vector<T>& tmp){std::swap(_start, tmp._start);std::swap(_finish, tmp._finish);std::swap(_endofstorage, tmp._endofstorage);}vector<T>& operator=(vector<T> v){swap(v);return *this;}const T& operator[](size_t i) const{assert(i < size());return _start[i];}void resize(size_t n, T val = T()){if (n <= size()){_finish = _start + n;}else{reserve(n);while (_finish < _start + n){*_finish = val;++_finish;}}}private:iterator _start = nullptr; iterator _finish = nullptr; iterator _endofstorage = nullptr; };
}
测试
#include <iostream>
#include <cassert>
#include "myvector.hpp"using namespace std;int main()
{using myvector::vector;cout << "[Test 1] default constructor" << endl;vector<int> v;cout << " size=" << v.size() << " capacity=" << v.capacity() << endl;assert(v.size() == 0);cout << " -> OK\n";cout << "[Test 2] push_back and basic access" << endl;for (int i = 1; i <= 5; ++i){v.push_back(i);cout << " pushed " << i << " size=" << v.size() << " capacity=" << v.capacity() << "\n";}assert(v.size() == 5);for (size_t i = 0; i < v.size(); ++i){assert(v[i] == (int)i + 1);}cout << " contents:";for (auto x : v) cout << ' ' << x;cout << "\n -> OK\n";cout << "[Test 3] insert in middle" << endl;auto it = v.begin() + 2; cout << " before insert size=" << v.size() << " capacity=" << v.capacity() << "\n";v.insert(it, 99);cout << " after insert size=" << v.size() << " capacity=" << v.capacity() << "\n";assert(v.size() == 6);assert(v[2] == 99);cout << " contents:";for (auto x : v) cout << ' ' << x;cout << "\n -> OK\n";cout << "[Test 4] copy constructor" << endl;vector<int> vcopy(v);cout << " original size=" << v.size() << " copy size=" << vcopy.size() << "\n";assert(vcopy.size() == v.size());for (size_t i = 0; i < v.size(); ++i) assert(vcopy[i] == v[i]);cout << " -> OK\n";cout << "[Test 5] assignment operator" << endl;vector<int> vassign;vassign = v;cout << " assigned size=" << vassign.size() << "\n";assert(vassign.size() == v.size());for (size_t i = 0; i < v.size(); ++i) assert(vassign[i] == v[i]);cout << " -> OK\n";cout << "[Test 6] initializer_list constructor" << endl;vector<int> vil = {10, 20, 30};cout << " vil size=" << vil.size() << " contents:";for (auto x : vil) cout << ' ' << x;cout << "\n";assert(vil.size() == 3);assert(vil[0] == 10 && vil[1] == 20 && vil[2] == 30);cout << " -> OK\n";cout << "[Test 7] reserve and capacity growth" << endl;size_t oldcap = v.capacity();cout << " old capacity=" << oldcap << "\n";v.reserve(oldcap + 10);cout << " new capacity=" << v.capacity() << "\n";assert(v.capacity() >= oldcap + 10);cout << " -> OK\n";cout << "[Test 8] resize larger and smaller" << endl;v.resize(10, -1);cout << " resized up size=" << v.size() << " capacity=" << v.capacity() << "\n";assert(v.size() == 10);for (size_t i = 6; i < 10; ++i) assert(v[i] == -1);v.resize(3);cout << " resized down size=" << v.size() << "\n";assert(v.size() == 3);cout << " -> OK\n";cout << "[Test 9] pop_back" << endl;cout << " before pop size=" << v.size() << "\n";int last = v[v.size() - 1];v.pop_back();cout << " after pop size=" << v.size() << " popped=" << last << "\n";assert(v.size() == 2);cout << " -> OK\n";cout << "[Test 10] iteration (const and non-const)" << endl;int sum = 0;cout << " vassign contents:";for (auto &x : vassign) { sum += x; cout << ' ' << x; }cout << "\n";const vector<int> &cref = vassign;int csum = 0;for (auto it2 = cref.begin(); it2 != cref.end(); ++it2) csum += *it2;cout << " sum=" << sum << " csum=" << csum << "\n";assert(sum == csum);cout << " -> OK\n";cout << "Final vassign contents:";for (auto x : vassign) cout << ' ' << x;cout << '\n';cout << "ALL TESTS PASSED\n";return 0;
}
