C++ STL -4 | vector

本文综合 C语言中文网的多篇资料(vector 基础详解vector 基本用法vector 常用操作vector 进阶机制vector 高级操作vector 底层实现),侧重于提示一些进阶向的 std::vector 的关键知识点。


一、vector 基础知识回顾

1. vector 的本质与核心特性

  • std::vector 是 STL 中的动态顺序容器,底层实现为动态数组,支持元素的自动扩容和高效的随机访问。
  • 存储空间连续,支持下标访问(operator[]),可与 C 数组无缝协作。
  • 相比 std::array(静态数组),vector 支持动态插入/删除,并自动管理内存[vector详解]

2. vector 的初始化与基本用法

  • 常见初始化方式:
    std::vector<int> v1;                // 空vector
    std::vector<int> v2(5, 99);         // 5个99
    std::vector<int> v3 = {1,2,3};      // 列表初始化
    std::vector<int> v4(v3);            // 拷贝构造
    
  • 常用操作:
    • push_back(val):尾部添加元素
    • pop_back():移除尾部元素
    • insert(pos, val):指定位置插入
    • erase(pos):移除指定位置元素
    • clear():清空所有元素
    • size()/empty():元素个数/是否为空
    • front()/back():首/尾元素
    • begin()/end():迭代器支持遍历

常用操作代码示例:

std::vector<int> v = {1, 2, 3};
v.push_back(4);       // 尾部添加元素
v.pop_back();         // 移除尾部元素
v.insert(v.begin(), 0); // 头部插入0
v.erase(v.begin());      // 移除第一个元素
v.clear();               // 清空所有元素
if(v.empty()) { /* ... */ }

std::cout << v.size() << ", " << v.capacity() << std::endl;
std::cout << v.front() << ", " << v.back() << std::endl;

遍历vector的三种常见方式:

for(size_t i = 0; i < v.size(); ++i) std::cout << v[i];
for(auto it = v.begin(); it != v.end(); ++it) std::cout << *it;
for(int x : v) std::cout << x;

更多用法见[vector基本用法][常用操作]


二、容量管理与性能优化

1. size() 与 capacity() 区别

std::vector<int> v;
v.reserve(100);
v.push_back(1);
std::cout << v.size();     // 1
std::cout << v.capacity(); // 100
  • size():当前已存储元素数量。
  • capacity():当前分配的内存最多能存多少元素(不再分配的情况下)。
  • reserve(n):预分配内存,提升批量插入效率,避免多次扩容。

2. 扩容机制与注意事项

  • 默认扩容通常为原容量 1.5~2 倍,具体依赖实现。
  • 扩容时会迁移所有元素,导致原有指针/引用/迭代器失效。
  • 频繁插入建议预分配空间(reserve),避免多次扩容带来的性能损耗。

3. 回收内存技巧

  • clear() 只清空元素,不释放 capacity。
  • swap()shrink_to_fit() 可以回收多余内存:
std::vector<int>().swap(v); // 回收所有空间
v.shrink_to_fit();          // 请求收缩
  • swap()技巧示例:
std::vector<int> big(10000, 1);
big.clear();
std::vector<int>().swap(big); // big容量变为0,释放大块内存

三、底层实现机制与高级特性

1. 底层结构

  • 内部为一段连续内存,常用3个指针/迭代器维护:起始、已用、已分配末尾。
  • 支持自定义 allocator 分配器。

2. 插入、删除、随机访问性能

  • 随机访问:O(1)
  • 尾部插入/删除:O(1) 均摊
  • 中间插入/删除:O(n)
  • 扩容/收缩:O(n)(涉及元素迁移)

3. swap() 的高阶用法

std::vector<int> a = {1,2,3}, b = {4,5};
a.swap(b);
// a: 4,5; b: 1,2,3
  • 交换两个 vector 内容,O(1) 操作。
  • 可用于释放内存,或高效重置 vector 状态。

4. emplace_back()

struct Foo { Foo(int, std::string); };
std::vector<Foo> v;
v.emplace_back(42, "hello"); // 直接在内存中构造,避免临时对象
  • 直接在末尾原地构造对象,避免不必要的拷贝/移动,提高性能。

四、vector 的特殊实现与陷阱

1. std::vector<bool> 的特化实现

  • std::vector<bool> 并不是普通的 vector<T>,而是标准库对 vector 模板的一个特化版本。
  • 其底层采用位压缩(bit-packing)技术,每个 bool 用 1 bit 表示,而不是 1 字节,从而极大节省空间。
  • 实际存储结构是字节数组(如 unsigned char* 或 unsigned int*),每 8 或 32 个 bool 共用一个字节或整型。

2. 代理引用类型与访问机制

  • 因为不是连续的 bool 内存,operator[]at() 等访问返回的是代理类(proxy object),而不是 bool&
  • 这个代理类模拟了 bool 的读写操作,但本质不是普通 bool 的引用。
  • 这意味着无法像普通 vector 一样获取元素的真实地址(如 &v[0] 得到的不是 bool*),且与某些泛型算法、接口不兼容。

示例:

std::vector<bool> vb = {true, false};
auto b = vb[0]; // 类型是代理,不是bool&
vb[1] = true;   // 实际是代理对象赋值
// 错误用法
// bool *p = &vb[0]; // 错误,类型不兼容

3. 性能与兼容性问题

  • 访问元素时需通过位运算,性能不一定优于 vector<char>vector<uint8_t>
  • 在多线程环境下,多个线程同时修改同一字节的不同 bit,可能出现竞态条件和数据污染。
  • 代理类的存在导致 vector<bool> 不能安全地将元素引用传递到容器外部或存储在其他结构中。

4. 典型易错用法

std::vector<bool> vb(10, true);
// &vb[0]; // 错误,不能转换为bool*
std::sort(vb.begin(), vb.end()); // 某些实现下会出错

5. 何时应避免/替代

  • 除非极度关注节省布尔数组空间且能接受代理特性,否则建议使用 vector<char>vector<uint8_t>std::bitset
  • C++ 社区普遍认为 vector<bool> 的设计为“黑历史”,很多权威书籍如《Effective STL》都建议避免使用。

五、易错点与开发建议

1. 指针、引用、迭代器失效

std::vector<int> v = {1,2,3};
int* p = &v[0];
v.push_back(4); // 可能导致p失效
  • 扩容、插入、删除操作后,相关引用和迭代器均可能失效,需重新获取。

2. clear() 不等于释放内存

  • 只重置 size,不释放 capacity,必要时用 swap 技巧回收空间。

3. 大量数据建议 reserve

std::vector<int> v;
v.reserve(1000000);
for(int i=0;i<1000000;++i) v.push_back(i);
  • 批量插入前建议 reserve(),显著提升性能。

4. vector 谨慎使用

  • 了解其原理,避免踩坑,必要时用 std::vector<char>std::bitset 替代。

六、进阶用法与最佳实践

  • 熟练使用迭代器配合 STL 算法(如 sort、find 等)。
std::sort(v.begin(), v.end());
auto it = std::find(v.begin(), v.end(), 42);
  • 合理利用批量操作(assign、insert、erase)。
std::vector<int> v1(5, 1), v2;
v2.assign(v1.begin(), v1.end());
  • 存储自定义类型时,优先实现高效的移动构造/赋值。
  • 内存敏感场合巧用 shrink_to_fit 或 swap。

七、参考文献

  1. C++ STL vector容器详解
  2. C++ vector容器的用法(附实例)
  3. C++ vector容器常用操作
  4. C++ vector进阶与底层机制
  5. C++ vector常用高级操作
  6. C++ vector底层实现机制
  7. 《Effective STL》
  8. 《C++ Primer》
  9. 《STL源码剖析》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

电子异术家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值