首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >vector 从入门到通透:构造遍历、容量管理与实战技巧全梳理

vector 从入门到通透:构造遍历、容量管理与实战技巧全梳理

作者头像
用户11862565
发布2025-10-13 15:34:35
发布2025-10-13 15:34:35
2780
举报

前言

在C++标准模板库(STL)中,vector无疑是最常用且最重要的容器之一。它结合了数组的高效随机访问和动态内存管理的灵活性,是每个C++开发者必须掌握的工具。本文将带你全方位深入理解vector,从基本使用到底层实现原理,让你真正掌握这个强大的容器

一、vector的概述

1.1 什么是Vector?

在学习数据结构中我们已经实现了C语言版本的顺序表,在C++中Vector就是C语言中的顺序表,其是C++标准库的一个序列容器,能够动态增长和缩小,提供了对元素的随机访问能力。其底层实现是一个动态数组,这意味着元素在内存中是连续存储的。

主要特性:

1.动态数组:可根据需要自动调整大小

2.随机访问:支持O(1)时间的元素访问

3.尾部操作高效:在末尾插入和删除元素效率高

4.中间操作相对低效:在中间插入或删除元素需要移动后续元素

如果想要了解C语言版本实现的Vector可以点击我哦《【初阶数据结构】从 0 到 1 速通顺序表:C 语言实现 + 手撕算法(附完整代码)》 由于C++的STL的学习具有相似性,因此文章的大体框架类似

1.2 vector的定义和构造

在之前实现string时,C++设计的过于冗余,因此在实现Vector时便很好的解决了string的问题,下面我们通过对比string和Vector的文档,String文档vector文档 可以清楚看出vector更简洁

定义:

在这里插入图片描述
在这里插入图片描述

在C++中的定义,我们发现vector是模版实现的,那么就意味着,vector可以支持任何类型的数据

构造:

在这里插入图片描述
在这里插入图片描述

1.由于allocator是空间适配器,后续会介绍,这里仅把它当做默认构造

2 .用n个val构造

3 .迭代器区间构造

4 .拷贝构造

代码演示:

代码语言:javascript
复制
void test_vector1()
{
	//默认构造
	vector<int> v1;

	//n个val
	vector<int> v2(10, 1);

	//迭代器构造
	vector<int> v3(v2.begin(), v2.end());

	//拷贝构造
	vector<int> v4(v2);


}

那么构造学完了,根据string的学习过程来看,我们实现了打印遍历,在这里依旧如此,同样遍历有三种方式:

1.下标+

2 .迭代器遍历,这里仅演示正向迭代器,反向和常量迭代器可以看string的实现

3 .范围for遍历底层(底层是迭代器)

代码语言:javascript
复制
//下标+[ ]遍历
for (size_t i = 0 ; i < v3.size(); i++)
{
	cout << v3[i] << " ";
}
cout << endl;


//迭代器遍历
vector<int>::iterator it = v3.begin();
while (it != v3.end())
{
	cout << *it << " ";
	++it;
}

cout << endl;

//范围for遍历(底层还是迭代器)
for (auto e : v3)
{
	cout << e << " ";

}
cout << endl;
在这里插入图片描述
在这里插入图片描述

看完构造函数,那么我们还需要看下析构函数的实现

析构函数是自动调用的

在这里插入图片描述
在这里插入图片描述

二、vector容量操作

理解vector的容量管理机制对于编写高效代码至关重要:

2.1 容量增长机制

代码语言:javascript
复制
void TestVectorExpand()
{
	size_t sz;
	vector<int> v;
	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编译器中vector和string一样,依旧是1.5倍扩容,那么在gcc编译器是否也是2倍扩容呢?

答案是正确的:

在这里插入图片描述
在这里插入图片描述

2.2 reserve的各种情况分析

在了解完扩容机制后,我们同样希望不扩容,这时候我们的reserve就要登场了,这里我们直接reserve(100),就没有扩容了

在这里插入图片描述
在这里插入图片描述

下面我们来看看reserve的介绍:标准给出我们给出n,不一定开n,但一定大于等于n

在这里插入图片描述
在这里插入图片描述

和string,一样,在reserve时也会出现三种情况:

1.reserve的n大于capacity时,便会扩容

2 .reserve的n小于capacity,但是大于size,capacity并不会缩容(这里和string就有所不同了)

在这种情况下,string在vs编译器不会缩容,但是gcc会缩容,但是最多缩容到size,不会减少size()

在这里插入图片描述
在这里插入图片描述

在vector下,不管是哪个编译器平台都不会缩容

在这里插入图片描述
在这里插入图片描述

代码演示:

代码语言:javascript
复制
void test_vector2()
{
	//TestVectorExpand();

	vector<int> v(10, 1);
	v.reserve(20);
	//预计size是10,capacity是20
	cout << v.size() << endl;
	cout << v.capacity() << endl;

  //不缩容,不影响size
	v.reserve(15);
	cout << v.size() << endl;
	cout << v.capacity() << endl;

   //不缩容,不影响size
	v.reserve(5);
	cout << v.size() << endl;
	cout << v.capacity() << endl;
}

vs编译器下的:

在这里插入图片描述
在这里插入图片描述

gcc编译器下:

在这里插入图片描述
在这里插入图片描述

3 .reserve的n小于szie下也不会影响size

2.3 resize的介绍

在这里插入图片描述
在这里插入图片描述

resize和reverse一样,也是三种情况:

1.n小于size,在这种情况下,并不会缩容,但是会改变size,删除数据

2.size<n<capacity,插入数据,由于小于capacity,因此不会扩容

3.n>capacity,插入数据,由于小于capacity,因此会扩容

三、vector的修改器

3.1 insert(插入)实现

在这里插入图片描述
在这里插入图片描述

在这里insert不支持下标了,仅支持迭代器

代码演示:

代码语言:javascript
复制
void test_vector4()
{
	vector<int> v(10, 1);
	v.push_back(2);

	v.insert(v.begin(), 0);

	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	//在第三个位置插入数据
	v.insert(v.begin()+3, 10);

	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

3.2erase clear等的修改介绍

同样这里仅支持迭代器删除,和string的也是类似,这里通过代码演示更容易清楚vector的修改函数的特点

代码语言:javascript
复制
void test_modification() {
    vector<int> v = {1, 2, 3, 4, 5};
    
    // 尾部插入
    v.push_back(6);
    cout << "push_back后: ";
    for (auto e : v) cout << e << " ";
    cout << endl;
    
    // 尾部删除
    v.pop_back();
    cout << "pop_back后: ";
    for (auto e : v) cout << e << " ";
    cout << endl;
    
    // 指定位置插入
    v.insert(v.begin() + 2, 99);
    cout << "插入99后: ";
    for (auto e : v) cout << e << " ";
    cout << endl;
    
    // 删除指定位置元素
    v.erase(v.begin() + 2);
    cout << "删除99后: ";
    for (auto e : v) cout << e << " ";
    cout << endl;
    
    // 清空vector
    v.clear();
    cout << "clear后大小: " << v.size() << endl;
}

四、vector 容器核心特性、关键对比

4.1vector 的cin和cout

vector不支持流插入和流提取,因为vector的输入输出具有不确定性

下面我们可以自己实现下流插入和流提取

代码语言:javascript
复制
	vector<int> v1(5, 0);

	for (size_t i = 0; i < 5; i++)
	{
		cin >> v1[i];

	}


	for (auto e : v1)
	{
		cout << e << ", ";
	}
	cout << endl;
在这里插入图片描述
在这里插入图片描述

4.2vector与 string 的底层差异

我们思考个有趣的问题,可以用vector替换string吗?

答案是不可以,因为最底层的区别是string有‘\0’,vector没有,因此string可以很好的兼容C语言,但是vector不可以,这里不可以为vector加入’\0以满足替换string,因为一旦加入后,那么vector/vector那些中的"\0"的意思无法确定

vector除了可以存储内置类型以外,当然还可以存储自定义类型,如之前实现的日期类,string也可以,这是为了以后的vector可以存其他容器做铺垫,这是因为vector本身是由模板实现的

代码语言:javascript
复制
void test_vector5()
{
	vector<string> v1;
	string s1("xxxx");
	v1.push_back(s1);

	v1.push_back("yyyyyy");

	for (const auto& e : v1)
	{
		cout << e << " ";

	}
	cout << endl;
}

下面我们分析下vector<vector>代表什么?

我们便举一反三,可知,vector代表是顺序表是由一个个int对象组成的数组(一维数组)

那么vector<vector>就相当于数组里面存的对象是由一个个vector组成的元素,这里可以把它分解成我们学过的二维数组,既然数组中的每个元素都是vector,那么我们可以把其分解成二维数组,每一行代表其是由vector组成。

举个例子

代码语言:javascript
复制
void test_vector6()
{
	vector<int> v(5, 1);
	vector<vector<int>> vv(10, v);

	for (int i = 0; i < vv.size(); i++)
	{
		for (int j = 0; j < vv[i].size(); j++)
		{
			cout << vv[i][j] << " ";
		}
		cout << endl;
	}

}
在这里插入图片描述
在这里插入图片描述

4.3 vector实现杨辉三角

有人可能说这不也可以使用静态数组吗?为什么需要这么麻烦,答案是vector可以扩容,方便某些场景的使用,下面我们看个经典题目——杨辉三角

在这里插入图片描述
在这里插入图片描述

由于杨辉三角是需要动态开辟空间,并且每一行并不是规整的,因此我们发现使用C语言实现会过于麻烦,下面我们通过C++的vector来分析下:杨辉三角的每一行的头与尾均是1,从第三行开始的中间元素等于上方两个相邻元素相加

代码语言:javascript
复制
class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>>  vv(numRows);
        for(size_t i=0;i<numRows;++i)
        {
            vv[i].resize(i+1,1);
            // vv[i].reize(i+1,0);
            // //vv[i][0]=vv[i][vv[i].size()-1]=1;
            // vv[i].front()=vv[i].back();

        }
         
         //由于前两行首尾都是1,上面处理过了
        for(int i=2;i<vv.size();++i)
        {   
            //首尾不需要管
            for(int j=1;j<vv[i].size()-1;++j)
            {
                //该行元素等于上一行两个相邻元素相加
                vv[i][j]=vv[i-1][j-1]+vv[i-1][j];
            }
        }

           return vv;
    }
};

总结

以上便是vector大部分接口的优点和需要避坑的讲解,我们在学习STL的知识时,要多练多写,明白底层原理,下一章我们便来探究下vector的模拟实现,深层次了解vector的工作原理,敬请期待

源码链接

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-09-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、vector的概述
    • 1.1 什么是Vector?
    • 1.2 vector的定义和构造
  • 二、vector容量操作
    • 2.1 容量增长机制
    • 2.2 reserve的各种情况分析
    • 2.3 resize的介绍
  • 三、vector的修改器
    • 3.1 insert(插入)实现
    • 3.2erase clear等的修改介绍
  • 四、vector 容器核心特性、关键对比
    • 4.1vector 的cin和cout
    • 4.2vector与 string 的底层差异
    • 4.3 vector实现杨辉三角
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档