【C++】C++ vector 完全指南:构造、遍历、扩容、增删查改与二维数组
📌 相关文章推荐
很高兴你点开这篇文章✨
这里会持续更新我喜欢的内容,关注我,一起慢慢变好呀
👍 点赞 ⭐ 收藏 💬 评论
文章目录
前言
std::vector 是 C++ 标准库中最常用的序列容器,它封装了动态数组,能够自动管理内存扩容。相比于原生数组,vector 更安全、更灵活;相比于 string,vector 更加通用,可以存储任意类型的数据。
🐶 🐾 ✨ 🐾 🐶
1. vector 的构造与初始化
vector 提供了多种构造函数,满足不同场景的需求:
void test_vector1()
{
// 1. 默认构造:空 vector
vector<int> v1; // size = 0,capacity = 0
// 2. 指定大小和初始值:n 个元素,每个值为 val
vector<int> v2(5, 1); // {1, 1, 1, 1, 1},size = 5,capacity = 5
// 3. 拷贝构造:用已有 vector 创建新对象
vector<int> v3(v2); // v3 是 v2 的副本
// 4. 迭代器区间构造:截取 v2 中 [begin+1, end-1) 区间的元素
// 左闭右开区间:包含第一个元素之后,不包含最后一个元素
vector<int> v4(++v2.begin(), --v2.end());
// 原 v2: {1, 1, 1, 1, 1}
// ++v2.begin() 指向第2个元素(1)
// --v2.end() 指向最后一个元素之前(第4个元素)
// 所以 v4 包含第2~4个元素:{1, 1, 1}
}
注意 :迭代器区间构造是左闭右开的 [first, last),即包含 first 指向的元素,不包含 last 指向的元素。这种构造方式可以截取任意容器的子区间。
2. vector 的三种遍历方法
2.1 operator[ ] 下标遍历
for (size_t i = 0; i < v2.size(); i++)
{
cout << v2[i] << " ";
}
cout << endl;
🐶 🐾 ✨ 🐾 🐶
2.2 迭代器遍历
vector<int>::iterator it = v3.begin();
while (it != v3.end())
{
cout << *it << " "; // 解引用访问元素
it++;
}
cout << endl;
🐾 迭代器的用法和 string 完全一致,体现了 STL 容器的统一接口设计。
2.3 范围 for 遍历(C++11)
for (auto e : v4)
{
cout << e << " ";
}
cout << endl;
🐶 🐾 ✨ 🐾 🐶
3. 扩容机制:size、capacity 与 reserve
3.1 默认扩容机制测试
void TestVectorExpand()
{
size_t sz;
vector<int> v; // 空 vector,size=0,capacity=0
sz = v.capacity();
cout << "making v grow:\n";
for (int i = 0; i < 100; ++i) {
v.push_back(i); // 尾插元素
if (sz != v.capacity()) { // 容量发生变化时输出
sz = v.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
}
典型输出(VS 环境):
- 初始 capacity = 0
- 第1次扩容:capacity = 1
- 第2次扩容:capacity = 2
- 第3次扩容:capacity = 3
- 第4次扩容:capacity = 4
- 第5次扩容:capacity = 6
- 后续按 1.5 倍左右增长
3.2 reserve:预分配空间(不改变 size)
void test_vector2()
{
vector<int> v1(10, 1); // size=10, capacity=10
cout << v1.size() << endl; // 10
cout << v1.capacity() << endl; // 10
// 预分配 20 个空间
v1.reserve(20); // capacity 扩容到至少 20
cout << v1.size() << endl; // 10(不变)
cout << v1.capacity() << endl; // 20
// reserve 参数小于当前容量时,不会缩容(VS 行为)
v1.reserve(15); // capacity 仍是 20
v1.reserve(5); // capacity 仍是 20
}
重要规则:
reserve 只改变 capacity,不改变 sizereserve(n) 当 n > capacity 时才会扩容VS 环境下 reserve 不会缩容,g++ 可能会缩容
建议 :如果预先知道元素个数,用 reserve 预分配空间,避免频繁扩容
🐶 🐾 ✨ 🐾 🐶
4. resize:调整有效元素个数
resize 会改变 size,如果新大小超过原大小,会填充指定值:
vector<int> v2(10, 1); // {1,1,1,1,1,1,1,1,1,1}
// 扩大 size,多出的位置填充 2
v2.resize(15, 2); // {1,1,1,1,1,1,1,1,1,1,2,2,2,2,2}
cout << v2.size() << endl; // 15
cout << v2.capacity() << endl; // 可能变大(如 20)
// 缩小 size,删除多余元素
v2.resize(5); // {1,1,1,1,1}
cout << v2.size() << endl; // 5
cout << v2.capacity() << endl; // 容量不变(不会缩容)
resize vs reserve:
| 操作 | 是否改变 size | 是否改变 capacity | 用途 |
|---|---|---|---|
reserve(n) |
不变 | 可能增大 | 预分配空间,避免扩容 |
resize(n, val) |
改变, 变为 n | 改变,可能增大 | 调整元素个数 |
🐶 🐾 ✨ 🐾 🐶
5. 增删查改:push_back、insert、erase
5.1 尾插:push_back
vector<int> v1(5, 1); // {1,1,1,1,1}
v1.push_back(2); // {1,1,1,1,1,2}
5.2 插入:insert
// 头插:在 begin() 位置插入 3
v1.insert(v1.begin(), 3); // {3,1,1,1,1,1,2}
// 在指定位置插入:begin()+5 指向第6个元素之前
v1.insert(v1.begin() + 5, 4); // {3,1,1,1,1,4,1,2}
注意 :insert 只有迭代器版本,没有下标版本。v1.begin() + n 相当于第 n+1 个元素之前的位置。
5.3 删除:erase
// 删除区间 [first, last),左闭右开
// 删除下标 5 和 6 的元素(第6、7个)
v1.erase(v1.begin() + 5, v1.begin() + 7);
// 原:{3,1,1,1,1,4,1,2}
// 删掉 begin+5(4) 和 begin+6(1)
// 结果:{3,1,1,1,1,2}
注意 :erase 的区间是左闭右开 [first, last),即包含 first,不包含 last。
🐶 🐾 ✨ 🐾 🐶
6. 自定义类型在 vector 中的存储
vector 可以存储任意自定义类型,代码中使用 AA 类作为示例:
class AA
{
public:
AA(int a1 = 0, int a2 = 0)
: _a1(a1)
, _a2(a2)
{
}
int _a1 = 1; // 类内初始值(C++11),仅当构造函数未初始化时生效
int _a2 = 1; // 由于构造函数已初始化,实际值为构造函数的参数
};
void test_vector3()
{
// 使用列表初始化 vector
AA aa1; // 调用 AA(0,0)
vector<AA> vAA1 = { aa1, {0, 0}, {1, 1}, {2, 2} };
// 输出:0-0 0-0 1-1 2-2
// 遍历方式:迭代器
vector<AA>::iterator it1 = vAA1.begin();
while (it1 != vAA1.end()) {
// 注意:-> 运算符用于迭代器直接访问成员
cout << it1->_a1 << "-" << it1->_a2 << " ";
// 等价写法:cout << (*it1)._a1 << "-" << (*it1)._a2 << " ";
it1++;
}
cout << endl;
// 尾插:使用 {} 初始化临时对象
vAA1.push_back({ 3, 3 }); // {3,3}
// 尾插:使用命名对象
AA aa2(4, 4);
vAA1.push_back(aa2); // {4,4}
// 插入:在位置1插入 aa2
vAA1.insert(vAA1.begin() + 1, aa2); // {4,4} 插入到第二个位置
}
注意 :
类内初始值 =1 是 C++11 特性,仅当构造函数未初始化该成员时生效vector<AA> 中存储的是 AA 对象的副本,每次 push_back 都会调用拷贝构造使用 {a, b} 可以隐式构造 AA 对象(前提是构造函数不是 explicit)
🐶 🐾 ✨ 🐾 🐶
7. vector<string>:类类型的存储
void test_vector4()
{
vector<string> vs1;
string s1("xxxxx");
vs1.push_back(s1); // 存储 s1 的副本
// const char* 隐式转换为 string
vs1.push_back("yyyyy"); // 调用 string(const char*) 构造
// 使用 const auto& 避免拷贝
for (const auto& e : vs1) {
cout << e << " ";
}
// 输出:xxxxx yyyyy
cout << endl;
}
注意 :范围 for 中,如果元素类型较大(如 string),使用 const auto& 而不是 auto,避免每次循环都调用拷贝构造。
🐶 🐾 ✨ 🐾 🐶
8. 二维数组:vector<vector<int>>
vector 可以嵌套使用,实现动态二维数组:
void test_vector4()
{
// 创建一个 5 个 1 的 vector
vector<int> v(5, 1); // {1,1,1,1,1}
// 创建 10 行,每行都是 v 的副本(10×5 的二维数组)
vector<vector<int>> vv(10, v); // 10 行,每行 5 个 1
// 修改第 3 行第 2 列的元素为 2(下标从0开始)
vv[2][1] = 2;
// 双层循环遍历
for (size_t i = 0; i < vv.size(); i++) { // 遍历行
for (size_t j = 0; j < v.size(); j++) { // 遍历列
cout << vv[i][j] << " ";
}
cout << endl;
}
}
🐾 输出:
1 1 1 1 1
1 1 1 1 1
1 2 1 1 1
1 1 1 1 1
...
🐾 可以理解为一个动态二维数组,每行的长度可以不同(但示例中每行长度相同)。
🐶 🐾 ✨ 🐾 🐶
总结
std::vector 的核心使用场景:
| 分类 | 核心接口 |
|---|---|
| 构造 | vector()、vector(n, val)、拷贝构造、迭代器区间构造 |
| 遍历 | operator[]、迭代器、范围 for |
| 容量 | size()、capacity()、reserve()、resize() |
| 增删 | push_back()、insert()、erase() |
| 进阶 | 自定义类型存储、vector<string>、vector<vector<int>> 二维数组 |
关键要点回顾:
reserve 只预分配空间,不改变元素个数;resize 会改变元素个数VS 环境下 reserve 不会缩容,但 g++ 可能会使用 const auto& 遍历大对象,避免不必要的拷贝vector 可以嵌套实现多维数组,每行长度可以动态变化
🐶 🐾 ✨ 🐾 🐶
本文所有代码
🐾 Test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<string>
using namespace std;
void test_vector1()
{
//定义和初始化(构造函数)
vector<int> v1; //空vector -> size = 0,capacity = 0
vector<int> v2(5, 1); //含 5 个 1,size = 5,capacity = 5
vector<int> v3(v2); //拷贝构造,v3 是 v1 的副本
//迭代器初始化 -> 截取v2中“第一个元素之后”到“最后一个元素之前”的所有元素,来初始化v4
vector<int> v4(++v2.begin(), --v2.end());
//遍历
//operator[] 下标访问
for (size_t i = 0; i < v2.size(); i++)
{
cout<< v2[i] << " ";
}
cout << endl;
//迭代器 iterator 遍历
vector<int>::iterator it = v3.begin(); //和string一样需要通过vector来访问iterator
while (it != v3.end()) //当
{
cout << *it << " ";
it++;
//和string一样仍然是指针的写法
}
cout << endl;
//范围 for 遍历
for (auto e : v4)
{
cout << e << " ";
}
cout << endl;
}
//测试 vector 的默认扩容机制
void TestVectorExpand()
{
size_t sz;
vector<int> v; //创建一个int型的空vector->size=0,capacity=0
sz = v.capacity(); //获取当前容器的容量
cout << "making v grow:\n";
for (int i = 0; i < 100; ++i)
{
//尾插
v.push_back(i);
if (sz != v.capacity()) //每次插入后对比当前容量与初始容量sz,若发生变化则输出新容量
{
sz = v.capacity(); //计算新的容量
cout << "capacity changed: " << sz << '\n';
}
}
}
int main()
{
test_vector1();
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
void test_vector2()
{
//空间管理:size、capacity 与扩容策略
//TestVectorExpand();
vector<int> v1(10, 1);
cout << v1.size() << endl;
cout << v1.capacity() << endl;
cout << endl;
//预分配20,capacity=20
v1.reserve(20);
cout << v1.size() << endl;
cout << v1.capacity() << endl;
cout << endl;
//预分配15个空间,capacity=20
v1.reserve(15);
cout << v1.size() << endl;
cout << v1.capacity() << endl;
cout << endl;
//预分配5个空间,不够不会影响到空间容量,capacity=20
v1.reserve(5);
cout << v1.size() << endl;
cout << v1.capacity() << endl;
cout << endl;
vector<int> v2(10, 1);
for (auto e : v2)
{
cout << e << " ";
}
cout << endl;
//预分配15个空间,有多的就用2补上:1 1 1 1 1 1 1 1 1 1 2 2 2 2 2
v2.resize(15, 2);
for (auto e : v2)
{
cout << e << " ";
}
cout << endl;
//预分配5个空间:1 1 1 1 1
v2.resize(5);
for (auto e : v2)
{
cout << e << " ";
}
cout << endl;
}
int main()
{
test_vector2();
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void print(const vector<int>&v)//打印vector<int>容器内容
{
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
void test_vector3()
{
//内置类型的增删查改
vector<int> v1(5, 1); //1 1 1 1 1
print(v1);
//尾插
v1.push_back(2); //1 1 1 1 1 2
print(v1);
//头插
v1.insert(v1.begin(), 3); // 3 1 1 1 1 1 2
//insert只有迭代器的使用,头插的位置就是v1.begin()
print(v1);
//想要对指定位置插入数据只需要用首位置加常数即可
v1.insert(v1.begin() + 5, 4); // 3 1 1 1 1 4 1 2
print(v1);
cout << endl;
v1.insert(v1.begin() + 6, 4); // 3 1 1 1 1 4 4 1 2
print(v1);
cout << endl;
//删除
//begin()+5:指向容器第6个元素
//begin()+7:指向容器第8个元素
//这里是左闭右开的意思:[first,last)->左边是包含的,右边是不包含的,就删掉下标为5、6的元素,删不到第8个
v1.erase(v1.begin() + 5, v1.begin() + 7); // 3 1 1 1 1 1 2
print(v1);
}
int main()
{
test_vector3();
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////
//自定义类型的增删查改
class AA
{
public:
AA(int a1 = 0, int a2 = 0)
:_a1(a1) //_a1=a1=0
, _a2(a2) //_a2=a2=0
{
}
//类内初始值:=1是C++11特性,仅当构造函数未初始化该成员时生效
//当构造函数已初始化时,实际值就由构造函数决定,所以_a1=0,_a2=0
int _a1 = 1;
int _a2 = 1;
};
void test_vector3()
{
//C++规定:定义对象时,必须调用构造函数来初始化它,这里的构造函数是AA(int a1=0,int a2=0)
//这里已经带了默认参数,所以不传参也能调用:AA aa1=AA aa1(0,0)
AA aa1; //调用AA(0,0),因为默认参数a1=0,a2=0
vector<AA> vAA1 = { aa1, {0, 0}, {1, 1}, {2, 2} };
vector<AA>::iterator it1 = vAA1.begin();
while (it1 != vAA1.end())
{
cout << it1->_a1 << "-" << it1->_a2 << " ";
//cout << (*it)._a1 << "-" << (*it)._a2 << " "; //注意.的优先级比*高,需要括号
it1++;
}
cout << endl;
vAA1.push_back({ 3, 3 }); //{ aa1, {0, 0}, {1, 1}, {2, 2},{3, 3}};
AA aa2(4, 4);
vAA1.push_back(aa2); //{ aa1, {0, 0}, {1, 1}, {2, 2},{3, 3},{4, 4};
vAA1.insert(vAA1.begin() + 1, aa2);//{ aa1,{4, 4},{0, 0}, {1, 1}, {2, 2},{3, 3},{4, 4};
vector<AA>::iterator it2 = vAA1.begin();
while (it2 != vAA1.end())
{
cout << it2->_a1 << "-" << it2->_a2 << " ";
it2++;
}
}
int main()
{
test_vector3();
return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//类类型的增删查改
//vector里面存string类型数据
void test_voctor4()
{
vector<string> vs1;
string s1("xxxxx");
vs1.push_back(s1); //xxxxx
vs1.push_back("yyyyy"); //xxxxx yyyyy
//括号里面是类型是const char*,这里其实是走了隐式类型转换
//将const char*隐式类型转换成了string
for (const auto& e : vs1)
{
cout << e << " ";
}
/*for (auto e : vs1)
{
cout << e << " ";
}*/
//我们说过范围for底层就是迭代器
//但不同于内置类型,string类型数据拷贝给e时调用的是拷贝构造
//每取出一个数据就要进行拷贝构造的话代价就比较大了,所以我们可以使用引用来减少拷贝次数
cout << endl;
//vector里面存vector<int>类型数据(二维数组)
//10*5的二维数组:
vector<int> v(5, 1);
vector<vector<int>> vv(10, v);
vv[2][1] = 2; //将下标[2][1]的元素改成2
//operator[]下标打印
//i:行
//j:列
for (size_t i = 0; i < vv.size(); i++)
{
for (size_t j = 0; j < v.size(); j++)
{
cout << vv[i][j] << " ";
}
cout << endl;
}
cout << endl;
}
int main()
{
test_voctor4();
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
int main()
{
//test_vector1();
//test_vector2();
//test_vector3();
//TestVectorExpand();
return 0;
}
🐶 🐾 ✨ 🐾 🐶
- 欢迎留言交流
- 期待你的评论与建议
- 留下你的想法吧
谢谢你看到这里呀
如果喜欢这篇内容,点个关注,下次更新不迷路✨
👍 点赞 ⭐ 收藏 💬 评论
更多推荐


所有评论(0)