【C++ 容器】vector

C++11 中 std::vector 超详细用法

std::vector 是 C++ 标准库中最常用、最核心的动态数组容器,底层是连续内存存储,支持动态扩容,C++11 为其新增了移动语义、列表初始化、emplace 系列函数等关键特性,大幅提升了性能和易用性。

本文会覆盖 C++11 完整特性,从基础用法到高级操作、性能优化、常见坑点全讲解。

一、vector 核心特性

  1. 连续内存:和普通数组一样支持随机访问,[] 访问速度 O(1)
  2. 动态扩容:容量不足时自动分配更大内存,拷贝/移动原有元素
  3. C++11 新增:移动构造/赋值、列表初始化、emplace 操作、cbegin()/cend()
  4. 尾插/尾删:效率极高 O(1),中间插入/删除效率低 O(n)
  5. 线程不安全:多线程读写需要加锁

头文件:

#include <vector>
using namespace std; // 日常开发常用,也可写 std::vector

二、C++11 vector 初始化方式(重点)

C++11 新增列表初始化,写法更简洁。

1. 空 vector

vector<int> v1; 
vector<int> v2{}; // C++11 列表初始化空容器

2. 指定大小

vector<int> v(5);    // 5 个 int,默认值 0
vector<int> v{5};    // 注意!列表初始化:1 个元素,值为 5

3. 指定大小 + 初始值

vector<int> v(5, 10); // 5 个 10

4. 列表初始化(C++11 最常用)

vector<int> v = {1,2,3,4,5}; 
vector<int> v{1,2,3,4,5};   // 等价,省略 =

5. 拷贝/移动初始化

vector<int> v1 = {1,2,3};
vector<int> v2(v1);        // 拷贝
vector<int> v3 = move(v1); // C++11 移动初始化(v1 变为空)

6. 迭代器区间初始化

int arr[] = {1,2,3};
vector<int> v(arr, arr+3);

三、元素访问

vector 提供多种安全/高效的访问方式:

方法说明越界行为
v[i]随机访问,最快不检查越界,未定义行为
v.at(i)带边界检查out_of_range 异常
v.front()首元素空容器未定义行为
v.back()尾元素空容器未定义行为
v.data()C++11 返回底层数组指针

示例:

vector<int> v = {10,20,30};

v[0] = 100;        // 直接修改
v.at(1) = 200;     // 安全访问

int* p = v.data(); // C++11 获取连续内存指针,兼容 C 语言接口

四、C++11 新增:emplace 系列(性能王者)

这是 C++11 对 vector 最重要的优化!

  • push_back创建对象 → 拷贝/移动到容器
  • emplace_back直接在容器内存里构造对象,无拷贝/移动,性能更高

用法对比

// 普通对象
vector<string> v;

// push_back:先创建临时对象,再移动
v.push_back("hello");  

// emplace_back:直接在 vector 内部构造,效率更高
v.emplace_back("hello"); 

自定义结构体更明显

struct Person {
    string name;
    int age;
    Person(string n, int a) : name(n), age(a) {}
};

vector<Person> v;

// push_back 需要先构造临时对象,再移动
v.push_back(Person("Alice", 20));

// emplace_back 直接传构造参数,原地构造!推荐
v.emplace_back("Bob", 25); 

同理还有:

  • emplace:指定位置原地构造
  • emplace_front:头部原地构造(vector 不推荐用,效率低)

五、增加元素

1. 尾部添加

vector<int> v;
v.push_back(1);    // 传统
v.emplace_back(2);  // C++11 推荐
v = {3,4,5};        // C++11 列表赋值

2. 指定位置插入

auto it = v.begin();
v.insert(it, 100);        // 开头插入
v.insert(v.end(), 200);   // 尾插
v.insert(v.begin(), 3, 5);// 插入 3 个 5

// C++11 插入初始化列表
v.insert(v.end(), {6,7,8}); 

六、删除元素

1. 删除尾部元素

v.pop_back();

2. 删除指定位置/区间

v.erase(v.begin()); // 删除第一个元素

// 删除区间 [first, last)
v.erase(v.begin()+1, v.end()); 

3. 清空所有元素

v.clear(); // 清空元素,容量不变

4. C++11 常用:按条件删除(erase-remove 习惯用法)

// 删除所有等于 3 的元素
vector<int> v = {1,2,3,3,4};
v.erase(remove(v.begin(), v.end(), 3), v.end());
v.erase(remove(v.begin(), v.end(), 3), v.end()); 简要解析(erase-remove 惯用法

需要头文件:#include <algorithm>

分步拆解
  1. remove(v.begin(), v.end(), 3)
    std::remove 不会删除容器元素
    它把容器里不等于3的元素向前移动覆盖,返回一个新的尾迭代器 new_end
    new_end 往后到 v.end() 的区域都是无效、待清除的残余数据。

示例:{1,2,3,3,4} → remove后变为 {1,2,4,?,?},返回指向第3个元素(4之后)的迭代器。

  1. v.erase(new_end, v.end())
    vector 的 erase 接收迭代器区间 [first, last)真正删除区间内所有元素,释放空间。

整体作用
删除 vector 中所有值等于3的元素。

关键重点
  1. 不能单独只用 remove,它只是移动元素,不改变容器 size()
  2. 顺序不能颠倒:先 remove 整理元素,再 erase 批量删除尾部垃圾;
  3. 时间复杂度 O(n),是 vector 删除指定值元素标准写法;
  4. 仅适合连续容器(vector/deque),无序容器不适用。
补充对比误区
// ❌ 错误写法(只能删除单个迭代器元素,无法批量删全部3)
for(auto it = v.begin(); it != v.end(); ){
    if(*it == 3) it = v.erase(it);
    else ++it;
}

erase-remove 相比循环逐个erase减少多次元素搬迁,性能更优


七、容量与大小(非常重要)

vector 有两个核心概念:

  • size实际元素个数
  • capacity已分配内存能容纳的最大元素数(扩容前不会变)

常用函数

v.size();       // 元素个数
v.empty();      // 是否为空
v.capacity();   // 容量
v.reserve(n);   // C++11 预分配 n 个元素内存,**减少扩容开销**
v.shrink_to_fit();// C++11 释放多余内存,capacity = size

重点:reserve 性能优化

如果知道数据量,提前 reserve 可以避免多次自动扩容

vector<int> v;
v.reserve(1000); // 一次性分配 1000 个空间,后面插入不会频繁扩容

八、C++11 迭代器用法

C++11 新增const 迭代器反向迭代器,支持范围 for 循环

1. 普通迭代器

for(auto it = v.begin(); it != v.end(); ++it) {
    *it *= 2;
}

2. const 迭代器(C++11)

不能修改元素,只读更安全:

for(auto it = v.cbegin(); it != v.cend(); ++it) {
    cout << *it << endl;
}

3. 反向迭代器

for(auto it = v.rbegin(); it != v.rend(); ++it) {
    // 倒序遍历
}

4. 范围 for 循环(C++11 最简洁)

// 只读
for(int x : v) cout << x << endl;

// 可修改(引用)
for(int& x : v) x *= 2;

// 高效只读(const 引用)
for(const int& x : v) cout << x << endl;

九、C++11 移动语义(vector 高性能关键)

C++11 之前 vector 赋值/传参只能拷贝,数据大时很慢。

C++11 支持移动:直接偷取原对象内存,O(1) 复杂度

vector<int> v1 = {1,2,3};
vector<int> v2 = move(v1); // 移动,v1 变为空

作为函数返回值时,编译器自动优化移动

vector<int> create() {
    vector<int> v = {1,2,3};
    return v; // C++11 自动移动,无需拷贝
}

十、vector 嵌套(二维数组)

C++11 初始化二维 vector 非常简洁:

// 3 行 4 列,初始值 0
vector<vector<int>> v(3, vector<int>(4, 0)); 

// C++11 列表初始化二维数组
vector<vector<int>> v = {
    {1,2},
    {3,4},
    {5,6}
};

遍历:

for(auto& row : v) {
    for(int x : row) {
        cout << x << " ";
    }
    cout << endl;
}

十一、常见坑点(必看)

  1. 迭代器失效

    • 插入/删除元素可能导致迭代器失效
    • 扩容会导致所有迭代器、指针、引用全部失效
      解决:操作后重新获取迭代器。
  2. (){} 初始化区别

    • vector<int> v(5) → 5 个元素
    • vector<int> v{5} → 1 个元素,值为 5
  3. 中间插入/删除效率极低
    vector 是连续内存,中间操作会移动大量元素,频繁操作请用 std::list

  4. 不要使用 v[i] 越界访问
    调试用 at(i),会抛异常方便排查。


十二、完整 C++11 示例代码

#include <iostream>
#include <vector>
using namespace std;

struct Person {
    string name;
    int age;
    Person(string n, int a) : name(std::move(n)), age(a) {}
};

int main() {
    // 1. 列表初始化
    vector<int> v = {1,2,3,4,5};
    
    // 2. 范围 for
    for(int x : v) cout << x << " ";
    cout << endl;
    
    // 3. emplace_back 原地构造
    vector<Person> pv;
    pv.emplace_back("Tom", 20);
    pv.emplace_back("Jerry", 18);
    
    // 4. 容量操作
    v.reserve(100);
    v.shrink_to_fit();
    
    // 5. 插入初始化列表
    v.insert(v.end(), {6,7,8});
    
    // 6. const 迭代器
    for(auto it = v.cbegin(); it != v.cend(); ++it) {
        cout << *it << " ";
    }
    
    return 0;
}

总结

  1. C++11 核心升级:列表初始化、移动语义、emplace 原地构造、范围 for、cbegin/cend
  2. 优先使用emplace_back > push_back,范围 for,reserve 预分配
  3. 最佳实践:尾插尾删、提前分配内存、避免中间操作、注意迭代器失效
  4. vector 适用场景:需要随机访问、元素个数变化、尾部频繁增删
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值