讲师中心 微信公众号
AI工具推荐 视频效率加速

C++如何使用STL向量vector存储数据

梦枫同学_5859

梦枫同学_5859

发布时间:2025-09-17 11:24:02

|

865人浏览过

|

来源于php中文网

原创

std::vector是动态数组,支持自动内存管理、随机访问和动态扩容,相比C数组更安全高效。1. 可通过声明初始化创建;2. 用push_back或emplace_back添加元素,后者原地构造更高效;3. 支持下标、at()和迭代器访问,at()具备边界检查;4. 提供pop_back、erase和clear删除元素;5. size表示元素数量,capacity为已分配内存容量,可用reserve预分配、shrink_to_fit释放多余空间。emplace_back适用于直接构造对象,push_back适合已有对象;合理使用reserve可减少内存重分配开销,避免迭代器失效问题。

c++如何使用stl向量vector存储数据

C++中,STL的

std::vector
是存储同类型数据序列的利器,本质上它是一个动态数组,能够自动管理内存,让我们在编程时省去不少麻烦。它将数据连续地存储在内存中,这使得它在随机访问(通过索引访问元素)时效率极高,同时,由于其动态特性,我们不必在编译时就确定其大小,可以根据程序运行的需要灵活增减元素。

解决方案

使用

std::vector
存储数据,核心在于理解其声明、添加/访问/删除元素以及容量管理。

1. 声明与初始化:

你可以直接声明一个空的

vector
:

立即学习“C++免费学习笔记(深入)”;

std::vector<int> myNumbers; // 存储整数的vector
std::vector<std::string> names; // 存储字符串的vector

或者在声明时进行初始化:

std::vector<int> initialNumbers = {10, 20, 30, 40}; // 使用初始化列表
std::vector<double> fixedSizeVector(5); // 创建一个包含5个double类型元素,默认值为0.0的vector
std::vector<char> filledVector(3, 'A'); // 创建一个包含3个'A'的vector

2. 添加元素:

最常用的方式是

push_back()
,它会在
vector
的末尾添加一个元素。

myNumbers.push_back(50); // myNumbers现在是 {10, 20, 30, 40, 50}
myNumbers.push_back(60); // myNumbers现在是 {10, 20, 30, 40, 50, 60}

对于自定义对象,

emplace_back()
通常更高效,因为它直接在
vector
内部构造对象,避免了额外的拷贝或移动。

struct Point {
    int x, y;
    Point(int _x, int _y) : x(_x), y(_y) {}
};
std::vector<Point> points;
points.emplace_back(1, 2); // 直接构造Point(1, 2)
points.push_back(Point(3, 4)); // 先构造Point(3, 4),再拷贝或移动到vector

3. 访问元素:

你可以像访问数组一样使用

[]
操作符,或者使用
at()
方法。

int firstNum = myNumbers[0]; // 访问第一个元素,不进行边界检查
int lastNum = myNumbers.back(); // 访问最后一个元素
int safeNum = myNumbers.at(1); // 访问第二个元素,会进行边界检查,越界会抛出std::out_of_range异常

迭代器也是访问元素的强大工具:

for (int num : myNumbers) { // C++11 范围for循环
    std::cout << num << " ";
}
std::cout << std::endl;

for (auto it = myNumbers.begin(); it != myNumbers.end(); ++it) { // 传统迭代器循环
    std::cout << *it << " ";
}
std::cout << std::endl;

4. 删除元素:

  • pop_back()
    :删除最后一个元素。
    myNumbers.pop_back(); // 删除了60
  • erase()
    :删除指定位置或范围的元素。
    myNumbers.erase(myNumbers.begin() + 1); // 删除第二个元素 (20)
    myNumbers.erase(myNumbers.begin(), myNumbers.begin() + 2); // 删除前两个元素
  • clear()
    :删除所有元素。
    myNumbers.clear(); // vector变为空

5. 大小与容量:

  • size()
    :返回
    vector
    中实际元素的数量。
  • capacity()
    :返回
    vector
    当前分配的内存能容纳的元素数量。
  • reserve(n)
    :预留至少能容纳
    n
    个元素的内存空间,可以减少后续的内存重新分配。
  • shrink_to_fit()
    :请求
    vector
    减少其容量以适应其当前大小。
std::vector<int> data;
data.reserve(100); // 预留100个元素的空间
for (int i = 0; i < 50; ++i) {
    data.push_back(i);
}
std::cout << "Size: " << data.size() << ", Capacity: " << data.capacity() << std::endl;
data.shrink_to_fit(); // 尝试将容量调整到50
std::cout << "After shrink_to_fit, Size: " << data.size() << ", Capacity: " << data.capacity() << std::endl;

std::vector
和普通数组有什么本质区别?为什么我应该选择
vector
而不是数组?

在我看来,

std::vector
和C风格的普通数组(如
int arr[10];
或动态分配的
int* arr = new int[10];
)最大的本质区别在于它们的内存管理方式和安全性。普通数组在声明时大小就固定了,或者动态分配后也需要手动
delete[]
来释放内存,这种固定大小和手动管理内存的模式,说实话,挺容易出问题的,比如缓冲区溢出、内存泄漏等。

std::vector
则完全不同。它是一个“智能”的动态数组,它的核心优势在于:

  1. 动态大小与自动内存管理: 这是最关键的一点。
    vector
    可以根据需要自动增长或缩小,你不需要担心内存分配和释放。当你
    push_back
    一个元素时,如果当前容量不足,
    vector
    会自动分配一块更大的内存,将现有元素移动过去,然后释放旧内存。这个过程完全由
    vector
    自己处理,遵循RAII(资源获取即初始化)原则,即便发生异常,内存也能得到妥善释放。这极大地减少了内存泄漏和悬挂指针的风险。
  2. 安全性:
    vector
    提供了
    at()
    方法进行边界检查。当你尝试访问一个越界的索引时,
    at()
    会抛出
    std::out_of_range
    异常,这比C风格数组的未定义行为(可能导致程序崩溃或更隐蔽的错误)要安全得多。虽然
    []
    操作符不进行边界检查,但至少你有了选择。
  3. 功能丰富: 作为STL的一部分,
    vector
    拥有丰富的成员函数(
    size()
    ,
    empty()
    ,
    clear()
    ,
    insert()
    ,
    erase()
    等),并且可以无缝地与STL的其他算法(如
    std::sort
    ,
    std::std::find
    等)配合使用。这些都是普通数组所不具备的。
  4. 迭代器支持:
    vector
    提供了迭代器,这让遍历和操作元素变得更加通用和灵活,也方便与STL算法集成。
  5. 类型安全:
    vector
    是模板类,它能确保只存储你指定类型的数据,提供了编译时的类型检查。

我个人觉得,除非在极度追求极致性能且内存分配模式非常固定、或是在与C语言库接口时,否则几乎所有情况下都应该优先选择

std::vector
。它的便利性、安全性和功能性,远超普通数组带来的那一点点可能的“裸金属”性能优势。毕竟,现代C++编程更注重效率和安全性,而不是徒增出错的概率。

push_back
和
emplace_back
在性能和使用场景上有什么不同?我什么时候该用哪个?

这俩兄弟都是往

vector
末尾添加元素,但它们在幕后做的事情,尤其是在处理复杂对象时,差异还是挺大的。说白了,区别就在于对象是如何被创建和放置到
vector
内部的
。

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载

push_back
的工作方式是:

  1. 你传入一个已经存在的对象,或者一个临时对象。
  2. push_back
    会拷贝(如果传入的是左值)或移动(如果传入的是右值,比如一个临时对象或
    std::move
    过的对象)这个对象到
    vector
    内部。 这意味着,如果你传入的是一个左值,会涉及到一次拷贝构造;如果你传入的是一个右值,会涉及到一次移动构造。在这之前,对象可能已经在别的地方构造了一次。

emplace_back
的工作方式是:

  1. 你传入的是构造目标对象所需的参数。
  2. emplace_back
    会直接在
    vector
    内部的内存空间上,原地构造这个对象。 它避免了额外的拷贝或移动操作,因为对象从一开始就直接在它最终应该在的位置被构造出来。

性能差异:

  • 对于基本数据类型(如
    int
    ,
    double
    ),或者那些拷贝/移动成本极低的轻量级对象,
    push_back
    和
    emplace_back
    的性能差异微乎其微,几乎可以忽略不计。
  • 对于复杂对象(比如有大量成员变量、需要分配内存、或者拷贝/移动构造函数开销很大的对象),
    emplace_back
    通常会带来显著的性能提升。因为它省去了至少一次拷贝或移动构造的开销。

使用场景:

  • 使用

    push_back
    :

    • 当你已经有一个现成的对象,想把它放到
      vector
      里时。
      MyObject obj("data");
      myVector.push_back(obj); // 拷贝
      myVector.push_back(std::move(obj)); // 移动
    • 当你需要将一个临时对象放入
      vector
      时,
      push_back
      也能很好地利用移动语义。
      myVector.push_back(MyObject("temp_data")); // 移动
    • 代码可读性可能略高,因为你直接看到了一个完整的对象被传入。
  • 使用

    emplace_back
    :

    • 首选当你需要将一个新创建的对象放入
      vector
      时。
      emplace_back
      能直接将构造函数的参数传递过去,在
      vector
      内部完成对象的构造,这是最有效率的方式。
      // 假设MyObject有一个构造函数MyObject(const std::string& name, int id)
      myVector.emplace_back("new_item", 123); // 直接在vector内部构造MyObject
    • 当对象构造开销大,或者拷贝/移动语义复杂时,
      emplace_back
      能避免不必要的中间对象创建和资源操作。

总的来说,我的建议是:如果能用

emplace_back
,就优先使用它。它代表了更现代、更高效的C++编程范式。只有当你确实需要将一个已经存在的对象(左值)放入
vector
,或者出于某些特殊原因(比如为了代码清晰度,或者构造函数参数过多导致
emplace_back
的参数列表过长),才考虑
push_back
。即使是
push_back
,如果传入的是右值,编译器也会优化为移动语义,但
emplace_back
是直接在目标位置构造,理论上是最优的。

std::vector
的内存管理机制是怎样的?我应该如何理解它的容量(capacity)和大小(size)?

std::vector
的内存管理机制,说白了就是它背后有一套策略来动态地分配和释放内存,以适应元素数量的变化。这套机制是其强大和灵活的基石,但理解它对于写出高效的C++代码至关重要。

核心机制:内存重新分配 (Reallocation)

当

vector
需要添加新元素,而当前分配的内存空间(即容量)不足时,它会进行一次“重新分配”操作:

  1. 它会申请一块更大的内存空间(通常是当前容量的1.5倍或2倍,具体取决于STL实现)。
  2. 将当前所有元素从旧内存移动(或拷贝,如果元素不支持移动语义)到新内存。
  3. 释放旧的内存空间。

这个过程,尤其是当元素数量很多时,开销是相当大的。频繁的重新分配会导致性能下降,因为涉及到内存申请、数据移动和内存释放。

容量 (Capacity) 与 大小 (Size) 的理解:

这两个概念是

vector
内存管理的关键:

  • 大小 (Size):这是
    vector
    中实际存储的元素数量。
    vector::size()
    方法返回的就是这个值。它代表了你当前能访问到的有效元素的个数。比如,你
    push_back
    了5个元素,
    size()
    就是5。
  • 容量 (Capacity):这是
    vector
    当前已经分配的内存可以容纳的元素总数。
    vector::capacity()
    方法返回的就是这个值。它总是大于或等于
    size()
    。当
    size()
    达到
    capacity()
    时,下一次添加元素就会触发重新分配。

举个例子:

std::vector<int> v; // size = 0, capacity = 0 (或某个很小的初始值)
v.push_back(1);    // size = 1, capacity 可能变为 1
v.push_back(2);    // size = 2, capacity 可能变为 2 (如果之前是1) 或 4 (如果之前是2,增长2倍)
v.push_back(3);    // size = 3, capacity 可能保持 4
v.push_back(4);    // size = 4, capacity 保持 4
v.push_back(5);    // size = 5, capacity 触发重新分配,可能变为 8

你会发现,

capacity
的增长不是线性的,而是几何级的。这是为了摊平重新分配的成本,使得在平均情况下,
push_back
操作的复杂度是常数时间O(1)。

优化策略:

  • reserve(n)
    :
    如果你预先知道
    vector
    大概会存储多少元素,或者至少知道一个上限,那么在开始添加元素之前调用
    reserve(n)
    是一个非常好的习惯。它会一次性分配足够的内存,避免了后续多次重新分配的开销。这对于性能敏感的场景尤其重要。
  • shrink_to_fit()
    :
    当你从
    vector
    中删除了大量元素,导致
    size
    远小于
    capacity
    时,
    vector
    并不会自动释放多余的内存。如果你希望
    vector
    释放这些未使用的内存,让
    capacity
    尽可能接近
    size
    ,可以调用
    shrink_to_fit()
    。但要注意,这个操作不保证一定会减少容量,标准只是说“请求”减少。而且,这个操作本身也可能涉及到一次重新分配(将数据移动到更小的内存块)。所以,只在确实需要释放大量内存,且不打算再添加大量元素时才考虑使用。
  • 迭代器失效: 这是一个非常重要的副作用。任何导致
    vector
    重新分配内存的操作(例如
    push_back
    当容量不足时,
    insert
    ,
    erase
    ,
    clear
    等)都会使指向
    vector
    内部元素的迭代器、指针和引用失效。这意味着你不能在修改
    vector
    的同时,继续使用之前获取的迭代器、指针或引用,否则会导致未定义行为。

理解

capacity
和
size
,并合理利用
reserve
,能让你更好地掌控
vector
的内存使用,避免不必要的性能损耗,写出更健壮、更高效的代码。在我看来,这是使用
std::vector
时一个常常被忽视但又极其重要的细节。

热门AI工具

更多
UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

AionClaw
AionClaw Hot

AionClaw是一款面向办公、创作和编程任务的AI桌面智能体。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

相关专题

更多
数据类型有哪几种
数据类型有哪几种

数据类型有整型、浮点型、字符型、字符串型、布尔型、数组、结构体和枚举等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3091

2023.10.31

php数据类型
php数据类型

本专题整合了php数据类型相关内容,阅读专题下面的文章了解更多详细内容。

554

2025.10.31

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

522

2026.02.12

sort排序函数用法
sort排序函数用法

sort排序函数的用法:1、对列表进行排序,默认情况下,sort函数按升序排序,因此最终输出的结果是按从小到大的顺序排列的;2、对元组进行排序,默认情况下,sort函数按元素的大小进行排序,因此最终输出的结果是按从小到大的顺序排列的;3、对字典进行排序,由于字典是无序的,因此排序后的结果仍然是原来的字典,使用一个lambda表达式作为key参数的值,用于指定排序的依据。

1138

2023.09.04

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

6619

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3205

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

4148

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

2845

2025.08.29

Gradle插件开发入门教程
Gradle插件开发入门教程

本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

40

2026.10.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C++ Core Guidelines
C++ Core Guidelines

共0课时 | 0人学习

C++ Reference
C++ Reference

共0课时 | 0人学习

C++ 官方标准说明
C++ 官方标准说明

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn