欢迎光临
我们一直在努力

【C++】C++ vector 完全指南:构造、遍历、扩容、增删查改与二维数组

📌 相关专栏

  • 【Linux专栏】
  • 【C语言专栏】
  • 【测试专栏】
  • 【MySQL专栏】
  • 【C++ 专栏】

📌 相关文章推荐

  • 【C++】深度实战:模拟 String 类全部核心操作
  • 【C++】C++类与对象2:C++构造函数、运算符重载与流输入输出全面解析
  • 【Linux】 TCP进阶详解:字节流、粘包问题、异常情况与UDP完整对比2
  • 【测试】软件测试必读:一文搞懂BUG的生命周期与管理技巧

举爪爪打招呼

很高兴你点开这篇文章✨

这里会持续更新我喜欢的内容,关注我,一起慢慢变好呀

👍 点赞 ⭐ 收藏 💬 评论


文章目录

  • 前言
  • 1. vector 的构造与初始化
  • 2. vector 的三种遍历方法
    • 2.1 operator[ ] 下标遍历
    • 2.2 迭代器遍历
    • 2.3 范围 for 遍历(C++11)
  • 3. 扩容机制:size、capacity 与 reserve
    • 3.1 默认扩容机制测试
    • 3.2 reserve:预分配空间(不改变 size)
  • 4. resize:调整有效元素个数
  • 5. 增删查改:push_back、insert、erase
    • 5.1 尾插:push_back
    • 5.2 插入:insert
    • 5.3 删除:erase
  • 6. 自定义类型在 vector 中的存储
  • 7. vector<string>:类类型的存储
  • 8. 二维数组:vector<vector<int>>
  • 总结
  • 本文所有代码
    • 🐾 Test.cpp

前言

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,不改变 size
  • reserve(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)
    未经允许不得转载:171主机测评 » 【C++】C++ vector 完全指南:构造、遍历、扩容、增删查改与二维数组
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址