C++零基础到工程实战(4.3.5):vector元素空间和内存空间显示与控制

张开发
2026/4/20 23:09:22 15 分钟阅读

分享文章

C++零基础到工程实战(4.3.5):vector元素空间和内存空间显示与控制
目录一、前言二、本节代码示例三、什么是vector的元素空间和内存空间3.1 元素空间已经真正存在的元素个数3.2 内存空间已经分配好的容量大小3.3 一定要区分size不是capacity1size()2capacity()3.4 capacity显示的不是字节数四、vector空间控制中最常用的几个函数4.1 resize()改变元素数量4.2 reserve()预留内存容量4.3 resize() 和 reserve() 的本质区别1resize(n)2reserve(n)4.4 clear()清空元素4.5 shrink_to_fit()请求缩容五、为什么vector效率高但扩容也会有开销六、动态扩容实验分析6.1 这段代码的思路是什么6.2 这段代码优化6.3 为什么vector不会每次只扩1个空间七、本节重点总结7.1 size() 和 capacity() 的区别7.2 resize() 和 reserve() 的区别7.3 clear() 和 shrink_to_fit() 的区别7.4 vector效率的本质八、小结一、前言在前面的几篇内容中我们已经学习了vector的基础使用包括vector的定义与初始化元素访问与遍历搜索、删除、插入与排序到这里你已经知道vector是一个非常好用的动态数组容器。但如果想真正理解它为什么适合工程开发仅仅会“用”还不够还要进一步理解它背后的两个重要概念元素数量和内存容量。很多初学者在学习vector时最容易把下面这两个概念混在一起size()capacity()表面上看它们都和“大小”有关但其实它们表示的是两件完全不同的事情size()当前已经存了多少个元素capacity()当前已经分配了多少个元素位置的内存容量这两个概念搞清楚之后你才会真正理解为什么push_back()有时候很快有时候会慢一点为什么clear()后元素没了但内存不一定立刻变小为什么reserve()可以提前提高效率为什么resize()和reserve()虽然都能“变大”但本质完全不同本节我们就结合代码系统讲清楚vector的元素空间与内存空间显示和控制。二、本节代码示例先把本节要分析的核心代码放出来#include iostream #include vector using namespace std; int main() { vectorint v1; vectorint vdata(10); // 查看元素数量 cout v1.size(): v1.size() endl; cout vdata.size(): vdata.size() endl; // 查看容量大小 cout v1.capacity(): v1.capacity() endl; cout vdata.capacity(): vdata.capacity() endl; // resize改变元素数量 v1.resize(8); // reserve预留容量 v1.reserve(16); cout after resize and reserve\n; cout v1.size(): v1.size() endl; cout v1.capacity(): v1.capacity() endl; // push_back尾部新增元素 v1.push_back(99); for (auto v : v1) cout v |; cout endl; // clear清空元素 v1.clear(); cout after clear\n; cout v1.size(): v1.size() endl; cout v1.capacity(): v1.capacity() endl; // shrink_to_fit尝试缩容 v1.shrink_to_fit(); cout after shrink_to_fit\n; cout v1.size(): v1.size() endl; cout v1.capacity(): v1.capacity() endl; cout ----------------\n; // 观察动态扩容 int c v1.capacity(); for (int i 0; i 200; i) { v1.push_back(i); if (c ! v1.capacity()) { cout capacity from c to v1.capacity() endl; c v1.capacity(); } } return 0; }这段代码虽然不长但里面把vector的很多关键机制都串起来了非常适合用来理解vector的空间管理逻辑。三、什么是vector的元素空间和内存空间3.1 元素空间已经真正存在的元素个数vector中所谓的“元素空间”可以先理解为当前容器里真正已经存在、可以访问的元素数量。这个数量通过v.size()来查看。例如vectorint v1; vectorint vdata(10);此时cout v1.size(): v1.size() endl; cout vdata.size(): vdata.size() endl;输出为v1.size():0 vdata.size():10说明v1现在一个元素都没有vdata现在已经有 10 个元素了也就是说size()关心的是逻辑上有多少个元素存在。3.2 内存空间已经分配好的容量大小vector还有一个很重要的概念叫容量通过v.capacity()来查看。例如cout v1.capacity(): v1.capacity() endl; cout vdata.capacity(): vdata.capacity() endl;输出为v1.capacity():0 vdata.capacity():10这里表示v1当前没有元素也没有额外分配存储空间vdata当前不仅有 10 个元素而且已经分配了足够容纳 10 个元素的内存所以capacity()关心的是当前底层已经分配了多少“可容纳元素的位置”。3.3 一定要区分size不是capacity这是本节最核心的地方。size()和capacity()的区别可以直接理解为1size()当前已经存储的元素数量2capacity()当前已经分配好的内存容量表示最多还能在不重新扩容的情况下容纳多少元素比如vectorint v; v.resize(8); v.reserve(16);此时size()可能是 8capacity()可能是 16意思就是现在真正存在 8 个元素但底层已经预留了 16 个元素的位置也就是说后面再继续插入一些元素时只要不超过 16就不需要重新申请内存。3.4 capacity显示的不是字节数这里还要特别提醒一下capacity()返回的不是“字节数”而是能容纳多少个当前类型的元素。例如vectorint v; v.reserve(16);这里capacity()返回的是 16不是 64。虽然对于int来说如果每个int占 4 字节那么底层大致可能需要 16 × 4 64 字节左右的空间但capacity()本身显示的是“元素个数容量”不是字节数。四、vector空间控制中最常用的几个函数4.1 resize()改变元素数量代码v1.resize(8);这句的作用是把v1的元素数量改成 8。因为v1原来是空的所以执行完后v1中就会真正出现 8 个元素。对于int类型这些新元素通常会被默认初始化为0。所以执行完后v1的逻辑状态大致是0 0 0 0 0 0 0 0此时v1.size()为 8v1.capacity()至少也要能容纳 8 个元素所以resize()的关键点是它不只是调整内存更重要的是调整“元素个数”。4.2 reserve()预留内存容量代码v1.reserve(16);这句的作用是至少把底层容量扩充到 16。但是要注意reserve()只影响容量不影响当前元素数量。也就是说如果原来size() 8capacity() 8执行v1.reserve(16);之后通常会变成size() 8capacity() 16所以reserve()的本质是提前申请好内存避免后续频繁扩容。这在工程里非常常用因为它可以减少反复申请和搬移内存带来的性能开销。4.3 resize() 和 reserve() 的本质区别这是非常容易考、也非常容易写错的点。1resize(n)改变的是元素数量如果变大会新增元素如果变小会删掉多余元素2reserve(n)改变的是容量大小只保证底层内存至少够大不会新增元素不会改变size()可以直接记一句resize管“有几个元素”reserve管“底下准备多大空间”。4.4 clear()清空元素代码v1.clear();这句的作用是清空所有元素。执行后size()会变成 0但是要特别注意clear() 不保证释放底层容量。也就是说元素没了不代表容量一定也归零。比如清空前如果size() 9capacity() 16那么清空后很可能变成size() 0capacity() 16因为它只是把元素删除了但底层那块内存还可能保留着方便后续继续使用。4.5 shrink_to_fit()请求缩容代码v1.shrink_to_fit();这句的作用通常理解为让容器尽量把多余容量缩掉使容量更贴近当前元素数量.内存适应元素 有多少元素就分配多少内存例如当前size() 0capacity() 16执行v1.shrink_to_fit();之后很多实现中可能会变成size() 0capacity() 0但这里一定要严谨一点说明shrink_to_fit()是“请求缩容”不是“强制必须缩到多少”。调用shrink_to_fit()后容器会尝试释放多余内存使容量更贴近当前元素数量。五、为什么vector效率高但扩容也会有开销这是理解vector性能的关键。vector之所以访问快是因为它底层通常是连续内存空间和普通数组很像所以支持下标访问快遍历效率高CPU 缓存友好但是vector在不断push_back()时并不是每加一个元素就只做一件小事。如果当前容量已经满了就会发生1重新申请一块更大的内存2把原来元素搬过去3释放旧内存4再插入新元素这个过程就叫扩容。所以vector的效率特点可以总结为平时访问很快尾插一般也很快但一旦扩容会有额外开销这也是为什么如果你提前知道大概要放多少数据最好先用reserve()预留空间。六、动态扩容实验分析后面这段代码就是专门用来观察容量变化的int c 0; //元素空间动态变化 for (int i 0; i 200; i) { v1.push_back(i); if (c ! v1.capacity()) { cout v1.capacity(): c endl; } c v1.capacity(); }这段代码的核心目的是想观察随着元素不断增加vector的容量是如何变化的。6.1 这段代码的思路是什么1不断push_back(i)让v1里的元素越来越多2每插入一次就检查当前容量是否变化如果容量变化了说明刚刚发生了扩容3把扩容过程打印出来这样就能看到vector不是每次只增加 1 个容量而是会按某种增长策略扩容6.2 这段代码优化原代码里写的是if (c ! v1.capacity()) { cout v1.capacity(): c endl; } c v1.capacity();这里打印的是旧值c不是变化后的新容量。所以如果想让观察更直观建议改成int c v1.capacity(); for (int i 0; i 200; i) { v1.push_back(i); if (c ! v1.capacity()) { cout capacity from c to v1.capacity() endl; c v1.capacity(); } }这样输出会更清楚比如类似capacity from 0 to 1 capacity from 1 to 2 capacity from 2 to 4 capacity from 4 to 8 capacity from 8 to 16 ...当然不同编译器、不同标准库实现扩容策略可能略有不同但通常都不是“每次只加1”。6.3 为什么vector不会每次只扩1个空间如果每次插入一个元素都只扩容 1 个位置那么会频繁发生申请内存拷贝旧数据释放旧空间这样效率会非常差。所以vector通常会采用一种“按比例增长”的方式例如扩到 2 倍或者扩到更大的某个倍数这样就能减少扩容次数提高整体效率。这也是vector性能设计里非常重要的一点。七、本节重点总结7.1 size() 和 capacity() 的区别1size()当前真正存在的元素个数2capacity()当前底层已分配好的容量大小7.2 resize() 和 reserve() 的区别1resize(n)改变元素数量会真正新增或删除元素2reserve(n)改变容量大小只扩内存不新增元素7.3 clear() 和 shrink_to_fit() 的区别1clear()清空元素但不一定释放容量2shrink_to_fit()尝试缩小容量使其更贴近当前元素数量7.4 vector效率的本质1访问快因为底层是连续空间2尾插通常也快3扩容时会有额外开销4提前reserve()可以减少扩容次数提高效率八、小结本节我们从“能不能用 vector”进一步走到了“vector 为什么这么设计”。vector 不仅在管理元素还在管理一块可动态变化的连续内存空间。也正因为如此学习vector时一定要分清两件事现在有多少元素现在底层预留了多少空间

更多文章