举爪爪打招呼

很高兴你点开这篇文章✨

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

👍 点赞 ⭐ 收藏 💬 评论


前言

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>> 二维数组

关键要点回顾:

  1. reserve 只预分配空间,不改变元素个数;resize 会改变元素个数
  2. VS 环境下 reserve 不会缩容,但 g++ 可能会
  3. 使用 const auto& 遍历大对象,避免不必要的拷贝
  4. 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;
}

🐶 🐾 ✨ 🐾 🐶


  1. 欢迎留言交流
  2. 期待你的评论与建议
  3. 留下你的想法吧

举爪爪求关注

谢谢你看到这里呀

如果喜欢这篇内容,点个关注,下次更新不迷路✨

👍 点赞 ⭐ 收藏 💬 评论

更多推荐