C++ STL vector容器详解:从原理到实践

news2025/6/4 18:25:40

引言

亲爱的小伙伴们,今天我要和大家分享一个C++编程中的"神器"——vector容器!作为STL(标准模板库)中最常用的容器之一,vector就像是一个"超级数组",既有数组的高效随机访问特性,又能自动调整大小,让你告别固定大小数组的困扰。不管你是刚接触C++的新手,还是想深入理解vector实现原理的老手,这篇博客都能带给你新的收获!

一、什么是vector?

想象一下,你正在收集各种小玩具,但不确定最终会有多少个。用普通数组的话,你得提前决定一个上限,太小了不够用,太大了又浪费空间。这时候,vector就像一个神奇的收纳盒,它能根据你放入的玩具数量自动调整大小!

定义与特点

  • 动态数组:vector是一个能够存放任意类型的动态数组
  • 连续内存:vector中的元素在内存中连续存储,支持高效的随机访问
  • 自动扩容:当空间不足时,vector会自动分配更大的内存空间
  • 类型安全:通过模板机制确保类型安全

vector的内部结构

从实现原理上看,vector内部维护三个关键指针:

指向数据的起始位置 —— start

指向最后一个元素的下一个位置 —— finish

指向分配的内存空间的尾端 —— end_of_storage

!vector内部结构示意图

这就像一个能伸缩的宿舍:

  • start是宿舍的门口
  • finish是最后一名同学睡的床位后面一点的位置
  • end_of_storage是宿舍的墙壁(容量上限)

二、vector的基本操作

创建vector

#include <vector>

// 创建空vector

std::vector<int> vec1;

// 创建包含5个元素的vector,所有元素初始化为0

std::vector<int> vec2(5);

// 创建包含5个元素的vector,所有元素初始化为10

std::vector<int> vec3(5, 10);

// 使用初始化列表(C++11)

std::vector<int> vec4 = {1, 2, 3, 4, 5};

// 从数组创建vector

int arr[] = {1, 2, 3, 4, 5};

std::vector<int> vec5(arr, arr + 5);

// 从另一个vector创建

std::vector<int> vec6(vec5);

这就像不同的方式开party:可以先空着等人来(vec1),可以先邀请5个人(vec2),可以先邀请5个特定的朋友(vec3)...

添加和删除元素


std::vector<int> vec;

// 在尾部添加元素

vec.push_back(10);  // 添加元素10到尾部

// 在指定位置插入元素

vec.insert(vec.begin() + 1, 20);  // 在第二个位置插入20

// 删除尾部元素

vec.pop_back();

// 删除指定位置的元素

vec.erase(vec.begin() + 1);  // 删除第二个元素

// 清空所有元素

vec.clear();

想象一下排队买票:

  • push_back是有人来排队,自动站到队尾
  • insert是有人插队到指定位置
  • pop_back是最后一个人不想排了,离开了
  • erase是队伍中间某个人离开了
  • clear是突然下大雨,所有人都散了

访问元素

std::vector<int> vec = {10, 20, 30, 40, 50};

// 使用索引访问

int a = vec[2];  // 获取索引为2的元素(30)

// 使用at函数(带边界检查)

int b = vec.at(3);  // 获取索引为3的元素(40),越界会抛出异常

// 获取第一个和最后一个元素

int first = vec.front();  // 获取第一个元素(10)

int last = vec.back();    // 获取最后一个元素(50)

这就像找教室里的同学:直接叫号(索引)、点名确认(at)、找第一个和最后一个。

三、vector的容量管理

理解vector的容量管理是掌握其高效使用的关键。

size与capacity

  • size:当前vector中实际元素的个数
  • capacity:当前vector在不扩容的情况下最多能容纳的元素个数

std::vector<int> vec;

for(int i = 0; i < 10; i++) {

    vec.push_back(i);

    std::cout << "Size: " << vec.size() 

              << ", Capacity: " << vec.capacity() << std::endl;

}

可能的输出结果:

Size: 1, Capacity: 1

Size: 2, Capacity: 2

Size: 3, Capacity: 4

Size: 4, Capacity: 4

Size: 5, Capacity: 8

Size: 6, Capacity: 8

Size: 7, Capacity: 8

Size: 8, Capacity: 8

Size: 9, Capacity: 16

Size: 10, Capacity: 16

这就像一个聚会场地:

  • size是实际到场的人数
  • capacity是场地能容纳的最大人数
  • 当人数超过容量时,需要换一个更大的场地

扩容原理

当vector需要更多空间时:

  1. 分配一块更大的内存(通常是当前容量的1.5倍或2倍)
  1. 将现有元素复制到新内存
  1. 释放旧内存
  1. 更新内部指针

这个过程叫做重新分配(reallocation),它是耗时的操作,因为需要复制所有元素。

优化容量管理

// 预留空间,避免频繁扩容

vec.reserve(1000);  // 预分配至少能容纳1000个元素的空间

// 调整大小(会改变size)

vec.resize(100);    // 调整为100个元素,如果扩大则用默认值填充

// 减少容量以适应当前大小

vec.shrink_to_fit();  // C++11引入

  • reserve是提前租一个足够大的场地,避免中途换场地
  • resize是主动调整聚会人数
  • shrink_to_fit是在人到齐后,换一个刚好够用的小场地,节省费用

四、vector的迭代器

迭代器是访问容器元素的统一接口,就像"智能指针"。

std::vector<int> vec = {10, 20, 30, 40, 50};

// 使用迭代器遍历

for(std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {

    std::cout << *it << " ";  // 输出:10 20 30 40 50

}

// C++11引入的范围for循环(语法糖)

for(auto& element : vec) {

    std::cout << element << " ";  // 输出同上

}

// 反向迭代器

for(std::vector<int>::reverse_iterator rit = vec.rbegin(); 

    rit != vec.rend(); ++rit) {

    std::cout << *rit << " ";  // 输出:50 40 30 20 10

}

这就像参观博物馆:

  • 普通迭代器是从入口到出口的参观路线
  • 反向迭代器是从出口回到入口的路线
  • 范围for循环就是跟着导游走,不用担心路线

五、vector的常见陷阱与优化

1. 迭代器失效问题

当vector扩容或删除元素时,迭代器可能会失效:

std::vector<int> vec = {1, 2, 3, 4, 5};

auto it = vec.begin() + 2;  // 指向元素3

vec.insert(vec.begin(), 0);  // 可能导致扩容

// 此时it已失效,使用it将导致未定义行为

解决方法:在可能导致迭代器失效的操作后,重新获取迭代器。

2. 不必要的复制

// 低效的循环添加元素

std::vector<int> vec;

for(int i = 0; i < 10000; i++) {

    vec.push_back(i);  // 可能多次扩容

}

// 优化:预先分配足够空间

std::vector<int> vec2;

vec2.reserve(10000);  // 提前分配空间

for(int i = 0; i < 10000; i++) {

    vec2.push_back(i);  // 避免扩容

}

3. 返回值优化

// 避免不必要的复制

void processVector(const std::vector<int>& vec) {

    // 使用const引用作为参数

}

std::vector<int> createVector() {

    std::vector<int> result = {1, 2, 3};

    return result;  // 编译器会优化,避免不必要的复制

}

六、实际应用案例

案例1:动态二维数组

// 创建10x20的二维数组

std::vector<std::vector<int>> matrix(10, std::vector<int>(20, 0));

// 访问元素

matrix[5][10] = 100;

// 动态调整行数

matrix.resize(15);  // 现在是15x20

案例2:高效字符串分割



std::vector<std::string> split(const std::string& str, char delimiter) {

    std::vector<std::string> tokens;

    std::string token;

    std::istringstream tokenStream(str);

    

    while (std::getline(tokenStream, token, delimiter)) {

        tokens.push_back(token);

    }

    

    return tokens;

}

案例3:自定义对象的存储

struct Student {

    std::string name;

    int age;

    

    Student(const std::string& n, int a) : name(n), age(a) {}

};

std::vector<Student> students;

students.push_back(Student("小明", 18));

students.push_back(Student("小红", 17));

// C++11的emplace_back可以在容器内直接构造对象,避免复制

students.emplace_back("小刚", 19);

总结

vector是C++中最常用的容器之一,它提供了数组的高效随机访问,又克服了数组大小固定的限制。通过深入理解vector的工作原理,你可以更高效地使用它,避免常见陷阱,写出更优雅高效的代码。

记住:合理使用reserve预分配空间、避免不必要的复制、注意迭代器失效问题,这些都是使用vector的关键技巧!

在下一篇博客中,我们将更深入地探讨vector的内存管理机制和更多高级用法。敬请期待!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2396804.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【论文笔记】Transcoders Find Interpretable LLM Feature Circuits

Abstract 机制可解释性(mechanistic interpretability)的核心目标是路径分析(circuit analysis)&#xff1a;在模型中找出与特定行为或能力对应的稀疏子图。 然而&#xff0c;MLP 子层使得在基于 Transformer 的语言模型中进行细粒度的路径分析变得困难。具体而言&#xff0c;…

每天总结一个html标签——a标签

文章目录 一、定义与使用说明二、支持的属性三、支持的事件四、默认样式五、常见用法1. 文本链接2. 图片链接3. 导航栏 在前端开发中&#xff0c;a标签&#xff08;锚点标签&#xff09;是最常用的HTML标签之一&#xff0c;主要用于创建超链接&#xff0c;实现页面间的跳转或下…

android binder(1)基本原理

一、IPC 进程间通信&#xff08;IPC&#xff0c;Inter-Process Communication&#xff09;机制&#xff0c;用于解决不同进程间的数据交互问题。 不同进程之间用户地址空间的变量和函数是不能相互访问的&#xff0c;但是不同进程的内核地址空间是相同和共享的&#xff0c;我们可…

行业分析---小米汽车2025第一季度财报

1 背景 最近几年是新能源汽车的淘汰赛&#xff0c;前短时间比亚迪再次开始了降价&#xff0c;导致一片上市车企的股价大跌&#xff0c;足见车圈现在的敏感度。因此笔者会一直跟踪新势力车企的财报状况&#xff0c;对之前财报分析感兴趣的读者朋友可以参考以下博客&#xff1a;…

边缘计算网关支撑医院供暖系统高效运维的本地化计算与边缘决策

一、项目背景 医院作为人员密集的特殊场所&#xff0c;对供暖系统的稳定性和高效性有着极高的要求。其供暖换热站传统的人工现场监控方式存在诸多弊端&#xff0c;如人员值守成本高、数据记录不及时不准确、故障发现和处理滞后、能耗难以有效监测和控制等&#xff0c;难以满足…

简单了解string类的特性及使用(C++)

string的特性 string类不属于STL&#xff0c;它属于标准库 但由于它具有数据结构的特性&#xff0c;所以从归类的角度&#xff0c;可以将string类归类到容器里面去 在C标准库中&#xff0c;std::string 是一个特化的类型&#xff0c;实际上是 std::basic_string 的别名。std…

FastAPI+Pyomo实现线性回归解决饮食问题

之前在 FastAPI介绍-CSDN博客 中介绍过FastAPI&#xff0c;在 Pyomo中线性规划接口的使用-CSDN博客 中使用Pyomo解决饮食问题&#xff0c;这里将两者组合&#xff0c;即FastAPI在服务器端启动&#xff0c;通过Pyomo实现线性回归&#xff1b;客户端通过浏览器获取饮食的最优解。…

16.FreeRTOS

目录 第1章 FreeRTOS 实时操作系统 1.1 认识实时操作系统 1.1.1 裸机的概念 1.1.2 操作系统的概念 1.2 操作系统的分类 1.3 常见的操作系统 1.4 认识实时操作系统 1.4.1 可剥夺型内核与不可剥夺型内核 1.4.2 嵌入式操作系统的作用 1.4.3 嵌入式操作系统的发展 1.4.4…

Redis最佳实践——购物车优化详解

Redis在电商购物车高并发读写场景下的优化实践 一、购物车业务场景分析 典型操作特征 读/写比例 ≈ 8:2高峰QPS可达10万单用户最大商品数500操作类型&#xff1a;增删改查、全选/反选、数量修改 技术挑战 高并发下的数据一致性海量数据存储与快速访问实时价格计算与库存校验分…

【计算机网络】传输层UDP协议

&#x1f525;个人主页&#x1f525;&#xff1a;孤寂大仙V &#x1f308;收录专栏&#x1f308;&#xff1a;计算机网络 &#x1f339;往期回顾&#x1f339;&#xff1a; 【计算机网络】应用层协议Http——构建Http服务服务器 &#x1f516;流水不争&#xff0c;争的是滔滔不…

安全漏洞修复导致SpringBoot2.7与Springfox不兼容

项目基于 springboot2.5.2 实现的&#xff0c;用 springfox-swagger2 生成与前端对接的 API 文档&#xff1b;pom.xml 中依赖如下 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId>&l…

从法律层面剖析危化品证书:两证一证背后的安全逻辑

《安全生产法》第 24 条明确规定&#xff0c;危化品单位主要负责人和安全管理人员 “必须考核合格方可上岗”。这并非仅仅是行政要求&#xff0c;而是通过法律来筑牢安全防线。在某危化品仓库爆炸事故中&#xff0c;由于负责人未持证&#xff0c;导致事故责任升级&#xff0c;企…

深入理解复数加法与乘法:MATLAB演示

在学习复数的过程中&#xff0c;复数加法与乘法是两个非常基础且重要的概念。复数的加法和乘法操作与我们常见的实数运算有所不同&#xff0c;它们不仅涉及到数值的大小&#xff0c;还有方向和相位的变化。在这篇博客中&#xff0c;我们将通过MATLAB演示来帮助大家更好地理解复…

【设计模式-3.6】结构型——桥接模式

说明&#xff1a;本文介绍结构型设计模式之一的桥接模式 定义 桥接模式&#xff08;Bridge Pattern&#xff09;又叫作桥梁模式、接口&#xff08;Interface&#xff09;模式或柄体&#xff08;Handle and Body&#xff09;模式&#xff0c;指将抽象部分与具体实现部分分离&a…

力扣题解654:最大二叉树

一、题目内容 题目要求根据一个不重复的整数数组 nums 构建最大二叉树。最大二叉树的构建规则如下&#xff1a; 创建一个根节点&#xff0c;其值为 nums 中的最大值。递归地在最大值左边的子数组前缀上构建左子树。递归地在最大值右边的子数组后缀上构建右子树。返回由 nums 构…

95套HTML高端大数据可视化大屏源码分享

概述​​ 在大数据时代&#xff0c;数据可视化已成为各行各业的重要需求。这里精心整理了95套高端HTML大数据可视化大屏源码&#xff0c;这些资源采用现代化设计风格&#xff0c;可帮助开发者快速构建专业的数据展示界面。 ​​主要内容​​ ​​1. 设计风格与特点​​ 采用…

scale up 不能优化 TCP 聚合性能

scale up 作为一种系统扩展优化的方法&#xff0c;旨在提高系统组件的执行效率&#xff0c;比如替换更高性能的硬件或算法。是否可以此为依据优化 TCP 呢&#xff0c;例如通过多条路径聚合带宽实现吞吐优化(对&#xff0c;还是那个 MPTCP)&#xff0c;答案是否定的。 因为 TCP…

Python-matplotlib库之核心对象

matplotlib库之核心对象 FigureFigure作用Figure常用属性Figure常用方法Figure对象的创建隐式创建&#xff08;通过 pyplot&#xff09;显式创建使用subplots()一次性创建 Figure 和 Axes Axes&#xff08;绘图区&#xff09;Axes创建方式Axes基本绘图功能Axes绘图的常用参数Ax…

Linux 脚本文件编辑(vim)

1. 用户级配置文件&#xff08;~/.bashrc&#xff09; vim ~/.bashrc # 编辑 source ~/.bashrc # 让编辑生效 ~/.bashrc 文件是 Bash Shell 的配置文件&#xff0c;用于定义用户登录时的环境变量、别名、函数等设置。当你修改了 ~/.bashrc 文件后&#xff0c;通常需要重新…

学习BI---基本操作---数据集操作

什么是数据集&#xff0c; 数据集&#xff08;Dataset&#xff09;​​ 是指从原始数据源&#xff08;如数据库、Excel、API等&#xff09;提取并经过标准化处理后的数据集合&#xff0c;通常以二维表形式存储&#xff0c;用于支撑报表、仪表盘等可视化分析。 数据集在QuickB…