📋 文章摘要
本文全面介绍了C++标准模板库(STL)中的std::vector动态数组容器。主要内容包括:
- vector基本概念:动态数组特性、连续内存存储、自动扩容机制
- 核心接口详解:构造与赋值、迭代器与遍历、resize/reserve、元素增删改查
- 关键问题解析:迭代器失效原理与正确处理方法
- 对比分析 :
vector<char>与string的异同与应用场景 - 性能优化 :
emplace_back与push_back的效率差异与使用技巧
vector作为STL中最常用的顺序容器,掌握其原理和正确用法对编写高效、安全的C++代码至关重要。
文章目录
-
- [📋 文章摘要](#📋 文章摘要)
- [一、vector 介绍](#一、vector 介绍)
- 二、vector接口
- 三、迭代器失效
- 四、vector<char>与string
- [五、emplace_back 与 push_back](#五、emplace_back 与 push_back)
一、vector 介绍
C++中的std::vector是标准模板库(STL)中最常用、最实用的动态数组容器。它解决了静态数组不能动态扩容的问题。vector可以自动扩容,并自动管理内存的释放。
vector的底层是一片连续的内存空间,因此它在逻辑上和物理上(内存地址)都是连续的。
cpp
//创建一个vector
vector<int> v;
二、vector接口
与string相比,vector的接口更加精简、冗余较少,且大多数接口与string类似。这里介绍一些常用接口。
构造
cpp
//构造容量为10的整型vector,默认用0值初始化
vector<int> v1(10);
//构造容量为10的字符vector,默认用'\0'初始化
vector<char> v2(10);
//使用迭代器范围构造
//构造6个元素的整型vector
vector<int> v3(v1.begin() + 2, v1.end() - 2);
//拷贝构造
vector<int> v4(v3);
//默认构造一个空的vector<string>
vector<string> v5;
//构造二维vector
vector<vector<int>> v6(3, v1); //3行10列的二维数组
//赋值示例
v6[2][1] = 1;
赋值
cpp
vector<int> v1(3, 1);
vector<int> v2 = v1;
vector<int> v3;
v3 = v2;
迭代器、遍历、访问
cpp
//begin返回首元素迭代器,end返回尾后迭代器
//支持反向迭代器、const迭代器、const反向迭代器
vector<int> v1(5, 9);
//迭代器遍历
vector<int>::iterator it = v1.begin();
while (it != v1.end())
{
cout << *it << " ";
it++;
}
cout << endl;
//范围for循环
for (auto i : v1)
{
cout << i << " ";
}
cout << endl;
//支持[]和at()进行元素访问
//for循环遍历
for (int i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
cout << endl;
//front返回头部元素,back返回尾部元素
for (int i = 0; i < v1.size(); i++)
{
v1[i] = i;
}
cout << v1.front() << endl;
cout << v1.back() << endl;
resize、reserve
cpp
//reserve 预留容量(改变容量但不改变大小)
vector<int> v1(10,1);
v1.reserve(20);//执行
cout << v1.size() << endl;
cout << v1.capacity() << endl;
v1.reserve(15);//不执行(当前容量已大于15)
cout << v1.size() << endl;
cout << v1.capacity() << endl;
v1.reserve(5);//不执行(当前容量已大于5)
cout << v1.size() << endl;
cout << v1.capacity() << endl;
//resize改变大小,新增元素可指定值初始化
cout << v1.size() << endl;
cout <<v1.capacity() << endl;
v1.resize(15);//执行
cout << v1.size() << endl;
cout << v1.capacity() << endl;
v1.resize(25);//执行,扩容
cout << v1.size() << endl;
cout << v1.capacity() << endl;
v1.resize(5);//执行
cout << v1.size() << endl;
cout << v1.capacity() << endl;
元素操作
cpp
vector<int> v1(10);
for (int i = 0; i < v1.size(); i++)
{
v1[i] = i;
}
cout << endl;
for (auto i : v1)
{
cout << i << " ";
}
cout << endl;
//删除尾部元素
v1.pop_back();
for (auto i : v1)
{
cout << v1[i] << " "; // 错误:这里应该是cout << i << " ";
}
cout << endl;
//在尾部插入元素
v1.push_back(1);
for (auto i : v1)
{
cout << i << " ";
}
cout << endl;
//在指定位置插入元素
v1.insert(v1.begin(),5);
for (auto i : v1)
{
cout << i << " ";
}
cout << endl;
//clear清空所有元素
v1.clear();
// 注意:清空后size为0,需要先添加元素才能访问
v1.resize(10);
//erase删除,使用迭代器指定位置
for (int i = 0; i < v1.size(); i++)
{
v1[i] = i;
}
for (auto i : v1)
{
cout << i << " ";
}
cout << endl;
//删除头部元素
v1.erase(v1.begin());
for (auto i : v1)
{
cout << i << " ";
}
cout << endl;
//删除前三个元素
v1.erase(v1.begin(), v1.begin() + 3);
for (auto i : v1)
{
cout << i << " ";
}
cout << endl;
//swap交换两个vector的内容
//...
三、迭代器失效
来看一下这两段代码。
cpp
//删除数据
vector<int> v = { 1,2,3,4,5,6 };
auto i = v.begin();
while (i != v.end())
{
if (*i % 2 == 0)
{
v.erase(i);
}
}

cpp
//删除数据
vector<int> v = { 1,2,3,4,5 };
auto i = v.begin();
++i;
v.insert(i, 2);
v.insert(i, 3);

运行之后都报错,为什么呢?
C++规定:当迭代器指向的元素被删除或插入操作影响后,该迭代器就失效了,我们不应再访问它。大多数编译器会进行检查。对于一些会使迭代器失效的接口(如erase、insert),它们会返回指向新位置的迭代器。再次强调:迭代器不一定是指针,它是一种抽象。
正确写法:
cpp
vector<int> v = { 1,2,3,4,5,6 };
auto i = v.begin();
while (i != v.end())
{
if (*i % 2 == 0)
{
i = v.erase(i);
}
else
{
i++;
}
}
cpp
vector<int> v = { 1,2,3,4,5 };
auto i = v.begin();
++i;
i = v.insert(i, 2);
v.insert(i, 3);
四、vector与string
vector<char>和string都是字符类型的顺序容器,且vector的接口更简洁,为什么还需要string呢?
- 在C++历史中,string的出现早于vector,编程语言的语法需要向前兼容,不能轻易删除。
- 在某些方面string的接口也确实更丰富。比如substr用于截取子字符串。
- string提供c_str()和data(),能返回以'\0'结尾的C风格字符串,方便与C语言字符串接口交互;
vector<char>不保证末尾有空字符,需要手动添加'\0'才能作为C字符串使用。
五、emplace_back 与 push_back
在接口中,emplace_back 和 push_back 的作用都是插入数据,用法几乎相同,但它们的底层实现是不同的且在一些特殊情况前者的效率要优于后者。


cpp
vector<vector<int>> vv;
// push_back
// 构造对象尾插
vector<int> v = { 1,2,3,4,5 };
vv.push_back(v);
//匿名对象尾插
vv.push_back(vector<int>({ 1,2,3,4,5,6 }));
// 都是一个构造 + 一个拷贝构造
//错误示范
//vv.push_back({1,2,3,4});//报错,但C++11 及以后支持
//vv.push_back(1, 2, 3, 4, 5);//同上
//emplace_back
vv.emplace_back(1,2,3,4,5,6);
// 只有一个构造
//错误示范
//vv.emplace({ 1,2,3,4,5 });//报错