当前位置: 首页 > news >正文

C++ - vector

文章目录

  • 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);使用迭代器进行初始化构造
vector() :无参数构造
#include <iostream>
#include <vector>
using namespace std;int main() {// 构造一个空的 vector,不包含任何元素vector<int> v1;cout << "v1 的大小: " << v1.size() << endl;cout << "v1 是否为空: " << (v1.empty() ? "是" : "否") << endl;return 0;
}

image-20251013090543309

vector(size_type n, const value_type& val =value_type()):构造并初始化n个val
#include <iostream>
#include <vector>
using namespace std;int main() {// 构造一个包含 5 个值为 10 的 int 类型 vectorvector<int> v2(5, 10);cout << "v2 的元素: ";for (int num : v2) {cout << num << " ";}cout << endl;return 0;
}

image-20251013090748528

vector (const vector& x) : 拷贝构造
#include <iostream>
#include <vector>
using namespace std;int main() {vector<int> v3 = {1, 2, 3, 4, 5};// 使用拷贝构造函数,构造一个与 v3 内容相同的 vector v4vector<int> v4(v3);cout << "v4 的元素: ";for (int num : v4) {cout << num << " ";}cout << endl;return 0;
}

image-20251013091011216

vector (InputIterator first, InputIterator last) :使用迭代器进行初始化构造
#include <iostream>
#include <vector>
#include <list>
using namespace std;int main() {list<int> l = {6, 7, 8, 9, 10};// 使用 list 的迭代器范围 [l.begin(), l.end()) 来构造 vector v5vector<int> v5(l.begin(), l.end());cout << "v5 的元素: ";for (int num : v5) {cout << num << " ";}cout << endl;return 0;
}

image-20251013091142685

iterator的使用

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

image-20251013092847538

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

image-20251013093218429

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

image-20251013093403220

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};// size:获取数据个数cout << "size: " << v.size() << endl;// capacity:获取容量大小cout << "capacity: " << v.capacity() << endl;// empty:判断是否为空cout << "empty: " << (v.empty() ? "是" : "否") << endl;// resize:改变 vector 的 sizev.resize(8, 10);  // 扩容到 8 个元素,新增元素值为 10cout << "resize 后 size: " << v.size() << endl;cout << "resize 后元素: ";for (int num : v) {cout << num << " ";}cout << endl;// reserve:改变 vector 的 capacityv.reserve(10);cout << "reserve 后 capacity: " << v.capacity() << endl;return 0;
}

image-20251013100425909

reserve缓解增容代价
#include <iostream>
#include <vector>
#include <chrono>  // 仅用于计时,不涉及其他容器
using namespace std;
using namespace chrono;int main() {// 情况1:不使用 reserve,依赖 vector 自动增容vector<int> v1;auto start1 = high_resolution_clock::now();  // 记录开始时间for (int i = 0; i < 1000000; ++i) {  // 插入100万个元素v1.push_back(i);// 每次容量不足时,vector 会重新分配更大的内存(通常是当前容量的2倍)// 并将原有元素复制到新内存,这个过程会消耗额外时间}auto end1 = high_resolution_clock::now();    // 记录结束时间cout << "不使用 reserve 耗时: " << duration_cast<microseconds>(end1 - start1).count() << " 微秒" << endl;// 情况2:使用 reserve 预先开辟足够空间vector<int> v2;v2.reserve(1000000);  // 提前申请能容纳100万个元素的空间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;
}

image-20251013101038637

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

image-20251013100916467

增删查改

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

image-20251013102149298

vector 迭代器失效问题

迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对 指针进行了封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器 底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即 如果继续使用已经失效的迭代器,程序可能会崩溃)。

插入操作导致迭代器失效
  • push_backemplace_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);  // 假设此时容量足够,迭代器it仍有效cout << *it << endl; v.push_back(5);  // 假设此时容量不足,重新分配内存,迭代器it失效// cout << *it << endl;  // 这会导致未定义行为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);  // 插入操作,假设未导致内存重新分配// cout << *it << endl;  // 迭代器it已失效,会导致未定义行为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();  // 迭代器it仍然有效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());  // 迭代器it已失效// cout << *it << endl;  // 会导致未定义行为return 0;
}
resizereserve 操作导致的迭代器失效
  • resize:调整vector的大小,如果新大小比原来小,超出新大小部分的迭代器会失效;如果新大小比原来大且需要重新分配内存,所有迭代器都会失效;如果新大小比原来大但不需要重新分配内存,插入新元素位置之后的迭代器会失效。
  • reserve:如果reserve的参数大于当前capacity,会导致内存重新分配,所有迭代器都会失效;如果reserve的参数小于等于当前capacity,迭代器不会失效。
避免迭代器失效的方法
  • 捕获插入或删除操作返回的迭代器inserterase操作会返回一个新的迭代器,指向插入或删除操作之后的位置,通过更新迭代器,可以确保迭代器的有效性。
  • 在循环中使用迭代器时特别注意:在对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;}// 改变vector的capacityvoid reserve(size_t n){if (n > capacity()){size_t oldSize = size();T *tmp = new T[n]; // 申请内存空间// 如果_start不是空指针,就释放之前申请的内存空间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;                     // 保存pos在旧数组中的位置reserve(capacity() == 0 ? 4 : capacity() * 2); // 申请新内存空间pos = _start + len;                            // 更新pos}// 插入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];}// 删除pos位置的数据iterator erase(iterator pos){assert(pos >= _start);assert(pos >= _finish);iterator i = pos + 1;while (i < _finish){*(i - 1) = *i;++i;}--_finish;return pos;}// vector 容器的迭代器范围构造函数,用于通过一个迭代器区间 [first, last) 来初始化 vector// int arr[] = {1, 2, 3, 4};// vector<int> v(arr, arr + 4); // 用数组的全部元素初始化 v// vector<int> v2(v.begin() + 1, v.end() - 1); // 用 v 中 [1,3) 范围的元素初始化 v2template <class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}//填充构造函数// 创建一个包含 5 个 int 类型元素 10 的 vector//vector<int> v1(5, 10); // 元素:10,10,10,10,10vector(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];}//改变 vector 容器中元素的数量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; // insert before element 3cout << "  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";// Print final contents of vassigncout << "Final vassign contents:";for (auto x : vassign) cout << ' ' << x;cout << '\n';cout << "ALL TESTS PASSED\n";return 0;
}

image-20251013152731734

http://www.dtcms.com/a/482647.html

相关文章:

  • 做百度竞价网站搜索不到百度北京总部电话
  • Process Monitor 学习笔记(5.5):保存并打开追踪记录(PML/CSV)与协作分享全攻略
  • 论MyBatis和JPA权威性
  • SAP MM采购订单创建接口分享
  • 基于单片机的简易智能衣架控制系统设计
  • rrk3588 与 NPU 主机下的异构通信:基于 PCIe 的设计与实现
  • 2025年--Lc185--63.不同路径II(动态规划,矩阵)--Java版
  • 跨境电商网站排行榜wordpress数据量大网站访问
  • 从零起步学习MySQL || 第四章:DQL语句定义及常见用法示例
  • 网站建设费如何核算.la域名的门户网站
  • 场景中的建筑静态物体转为actor,保持建筑的相对位置。
  • 数字孪生为什么需要5G?低延迟与高可靠实现精准控制
  • Idea 启动项目把启动类显示在左下角 并显示端口号
  • 网站网页模板网页设计培训哪家机构好
  • SLAM: 如何生成odom数据
  • 环境搭建node.js gnvm
  • 网站建设 就业方向东莞房价2021
  • Spring容器的实现
  • JWT 漏洞全解析:从原理到实战
  • 基于Redis6.2.8版本部署Redis Cluster集群
  • 工控一体机在智慧称重食堂中的应用
  • 网络包封装全解析:从字节流到数据帧
  • Spring MVC入门补充2
  • 石家庄站列车时刻表美食网站二级页面模板
  • GS016电动工具调速控制电路
  • Gartner:AI增强软件测试工具魔力象限报告精编(2025年10月)
  • 绵阳公司商务网站制作沈阳企业网站制作公司
  • elasticsearch-8.12.2集群部署
  • 【教程】增强版 print 函数,支持彩色与样式化终端输出
  • Python下载实战技巧技术文章大纲