C++ vector容器详解:从基础到实战应用
引言
在C++ STL(标准模板库)中,vector
是最常用的动态数组容器之一。它结合了数组的高效随机访问和动态扩展的灵活性,是处理动态数据集合的利器。本文将深入剖析vector
的各个核心功能,并通过代码示例和扩展分析,帮助读者全面掌握其使用方法及注意事项。
1. vector基本概念
功能:
vector
是一个动态数组,支持在尾部高效插入和删除元素,同时允许随机访问元素。它的数据结构和数组非常类似,也称为单端数组。
与普通数组的区别:
- 普通数组大小固定,而
vector
可动态扩展。 - 动态扩展机制:当容量不足时,
vector
会申请更大的内存空间(通常是原容量的2倍),拷贝原有元素并释放旧空间。这一过程会导致迭代器、指针和引用失效。
特性 | 普通数组 | vector容器 |
---|---|---|
内存管理 | 静态固定大小 | 动态扩展 |
越界检查 | 无 | at()方法提供 |
功能扩展 | 无 | 丰富的内置方法 |
内存效率 | 高 | 略低(有管理开销) |
使用便捷性 | 低 | 高 |
注:
- [动态扩展] 并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间。
- [迭代器]
vector
的迭代器是随机访问迭代器,支持快速随机访问容器中的元素。
2. vector构造函数
2.1 常用构造函数
- 默认构造函数:
vector<T> v;
创建一个空的vector
。 - 区间构造函数:
vector(v.begin(), v.end());
通过另一个vector
的区间来初始化。 - 填充构造函数:
vector(n, elem);
创建一个包含n
个elem
元素的vector
。 - 拷贝构造函数:
vector(const vector &vec);
通过另一个vector
来初始化。
2.2 示例代码
#include<iostream>
using namespace std;
#include<vector>// vector 构造函数
void Print(vector<int> &v)
{ for(vector<int>::iterator it=v.begin();it!=v.end(); it++){cout<<*it<<" ";}cout<<endl;
}void test01()
{vector<int>v1; // 默认构造 无参构造for(int i=0;i<10;i++){v1.push_back(i);}Print(v1);// 通过区间方式进行构造vector<int>v2(v1.begin(), v1.end());Print(v2);//n个elem方式构造vector<int>v3(10,100);Print(v3);// 拷贝构造vector<int>v4(v3);Print(v4);}int main()
{test01();system("pause");return 0;
}
总结:vector
提供了多种构造函数,用户可以根据需要选择合适的构造方式,灵活使用即可。
3. vector赋值操作
给vector容器进行赋值
3.1 常用赋值操作
- 重载等号操作符:
vector& operator=(const vector &vec);
- assign函数:
assign(beg, end);
或assign(n, elem);
3.2 示例代码
#include<iostream>
using namespace std;
#include<vector>// vector 赋值操作
/*
* vector& operator=(const vector &vec); //重载等号操作符
* assign(beg, end); //将[beg, end)区间中的数据拷贝赋值给本身。
* assign(n, elem); //将n个elem拷贝赋值给本身。
*/void Print(vector<int> &v)
{ for(vector<int>::iterator it=v.begin();it!=v.end(); it++){cout<<*it<<" ";}cout<<endl;
}void test01()
{vector<int>v1;for(int i=0; i<10; i++){v1.push_back(i);}Print(v1);// 赋值vector<int>v2;v2 = v1;Print(v2);// assignvector<int>v3;v3.assign(v1.begin(), v1.end());Print(v3);// n个elem 方式赋值vector<int>v4;v4.assign(10, 100);Print(v4);}int main()
{test01();system("pause");return 0;
}
内存变化说明:
- 赋值操作会完全替换原有内容
- 可能触发容量自动调整
- 旧元素会被正确销毁
总结:vector
的赋值操作简单直观,用户可以使用operator=
或assign
函数进行赋值。
4. vector容量和大小操作
对vector容器的容量和大小操作。
4.1 常用函数
- empty():判断容器是否为空。
- capacity():返回容器的容量。
- size():返回容器中元素的个数。
- resize(int num):重新指定容器的长度为num,若容器变长,则以默认值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除。
- resize(int num, elem):重新指定容器的长度为num,若容器变长,则以elem值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除。
4.2 示例代码
#include<iostream>
using namespace std;
#include<vector>// vector 容量和大小void Print(vector<int> &v)
{for(vector<int>::iterator it=v.begin(); it!=v.end(); it++){cout<<*it<<" ";}cout<<endl;
}void test01()
{vector<int>v1;for(int i=0; i<10; i++){v1.push_back(i);} Print(v1);// 判断容器是否为空if(v1.empty()) // 为真,代表容器为空{cout<<"v1为空"<<endl;}else{cout<<"v1不为空"<<endl;cout<<"v1的容量为:"<<v1.capacity()<<endl;cout<<"v1的大小为:"<<v1.size()<<endl; }// 重新指定大小v1.resize(15, 11); //利用重载版本,可以指定默认填充 Print(v1); //如果重新指定的比原来长了,默认用0填充新的位置v1.resize(5);Print(v1); //如果重新指定的比原来短了,超出的部分会删除
}int main()
{test01();system("pause");return 0;
}
容量与大小的区别:
- size:当前元素数量(逻辑大小)
- capacity:实际分配的内存容量(物理大小)
总结:
- 判断是否为空 --- empty
- 返回元素个数 --- size
- 返回容器容量 --- capacity
- 重新指定大小 --- resize
5. vector插入和删除操作
5.1 常用函数
- push_back(ele); //尾部插入元素ele
- pop_back(); //删除最后一个元素
- insert(const_iterator pos, ele); //迭代器指向位置pos插入元素ele
- insert(const_iterator pos, int count,ele); //迭代器指向位置pos插入count个元素ele
- erase(const_iterator pos); //删除迭代器指向的元素
- erase(const_iterator start, const_iterator end);//删除迭代器从start到end之间的元素
- clear(); //删除容器中所有元素
5.2 示例代码
#include<iostream>
using namespace std;
#include<vector>
// vector 插入和删除
/*push_back(ele); //尾部插入元素elepop_back(); //删除最后一个元素insert(const_iterator pos, ele); //迭代器指向位置pos插入元素eleinsert(const_iterator pos, int count,ele); //迭代器指向位置pos插入count个元素eleerase(const_iterator pos); //删除迭代器指向的元素erase(const_iterator start, const_iterator end); //删除迭代器从start到end之间的元素clear(); //删除容器中所有元素
*/
void Print(vector<int> &v)
{for(vector<int>::iterator it=v.begin(); it!=v.end();it++){cout<<*it<<" ";}cout<<endl;
}void test01()
{vector<int>v1;// 尾插法v1.push_back(9);v1.push_back(10);v1.push_back(34);v1.push_back(98);v1.push_back(135);Print(v1);// 尾删法v1.pop_back();Print(v1);// 插入 第一个参数是迭代器v1.insert(v1.begin(), 99);Print(v1);v1.insert(v1.begin(), 3, 100);Print(v1);// 删除 参数是迭代器v1.erase(v1.begin());Print(v1);// 清空// v1.erase(v1.begin(), v1.end());v1.clear();Print(v1);}int main()
{test01();system("pause");return 0;
}
性能注意事项:
- 尾部操作时间复杂度O(1)
- 中间插入/删除为O(n)
- 频繁插入建议使用list
总结:
- 尾插 --- push_back
- 尾删 --- pop_back
- 插入 --- insert (位置迭代器)
- 删除 --- erase (位置迭代器)
- 清空 --- clear
6. vector数据存取
6.1 常用函数
- at(int idx):返回索引
idx
所指的数据。 - operator[]:返回索引
idx
所指的数据。 - front():返回容器中第一个元素。
- back():返回容器中最后一个元素。
6.2 示例代码
#include<iostream>
using namespace std;
#include<vector>// vector 存取void test01()
{vector<int>v1;for(int i=0; i<10; i++){v1.push_back(i);}// 利用[]方式访问数组中元素for(int i=0; i<v1.size();i++){cout<<v1[i]<<" ";}cout<<endl;// 利用at方式访问数组中元素for(int i=0;i<v1.size();i++){cout<<v1.at(i)<<" ";}cout<<endl;// 获取第一个元素cout<<"第一个元素为:"<<v1.front()<<endl;// 获取最后一个元素cout<<"最后一个元素为:"<<v1.back()<<endl;}int main()
{test01();system("pause");return 0;
}
7. vector互换容器
7.1 功能
swap(vec)
函数用于交换两个vector
容器的内容。
7.2 示例代码
#include<iostream>
using namespace std;
#include<vector>void Print(vector<int> &v)
{for(vector<int>::iterator it=v.begin(); it!=v.end();it++){cout<<*it<<" ";}cout<<endl;
}// vector 互换容器
// 1.基本使用
void test01()
{vector<int>v1;for(int i=0; i<10;i++){v1.push_back(i);}cout<<"交换前:"<<endl;Print(v1);vector<int>v2;for(int i=10; i>0;i--){v2.push_back(i);}Print(v2);cout<<"交换后:"<<endl;v1.swap(v2);Print(v1);Print(v2);}
// 2.实际用途
// 巧用swap可以收缩内存空间
void test02()
{vector<int>v;for(int i=0;i<100000;i++){v.push_back(i);}cout<<"v的容量为:"<<v.capacity()<<endl;cout<<"v的大小为:"<<v.size()<<endl;v.resize(10); // 重新指定大小cout<<"v的容量为:"<<v.capacity()<<endl;cout<<"v的大小为:"<<v.size()<<endl;// 巧用swap收缩内存vector<int>(v).swap(v);cout<<"v的容量为:"<<v.capacity()<<endl;cout<<"v的大小为:"<<v.size()<<endl;}int main()
{test01();test02();system("pause");return 0;
}
总结:swap可以使两个容器互换,可以达到实用的收缩内存效果。
7.3 内存收缩(swap技巧)
vector<int>(v).swap(v); // 匿名对象技巧
原理分析:
- 创建临时匿名vector对象
- 利用拷贝构造函数进行元素拷贝
- 交换新旧vector的内容
- 临时对象析构释放多余内存
8. vector预留空间
8.1 功能
reserve(int len)
函数用于预留容器的容量,减少动态扩展的次数。
8.2 示例代码
#include<iostream>
using namespace std;
#include<vector>
// vector 预留空间void test01()
{vector<int>v;// 利用reserve预留空间v.reserve(100000);int num = 0; // 统计开辟次数int *p = NULL;for(int i=0; i<100000; i++){v.push_back(i);if(p!=&v[0]){p = &v[0];num++;}}cout<<"num = "<< num<<endl;}int main()
{test01();system("pause");return 0;
}
8.3 预分配内存(reserve)
v.reserve(100000); // 预先分配内存
适用场景:
- 已知元素大致数量时
- 需要大量push_back操作时
- 避免频繁内存重新分配
总结:reserve
函数可以有效减少vector
在动态扩展时的内存分配次数,提升性能。如果数据量较大,可以一开始利用reserve预留空间。
9. 实战应用技巧
9.1 性能优化
- 尽量使用emplace_back代替push_back
- 预分配足够内存减少扩容次数
- 排序时考虑使用算法库的sort
- 避免在循环中反复resize
9.2 常见陷阱
-
迭代器失效问题
- 插入/删除操作可能导致迭代器失效
- 解决方案:操作后重新获取迭代器
-
越界访问
// 错误示例 for(int i=0; i<=v.size(); ++i) cout << v[i]; // 可能越界
-
浅拷贝问题
vector<string*> v1; vector<string*> v2 = v1; // 指针拷贝,非深拷贝
9.3 最佳实践
- 遍历优先使用迭代器或范围for循环
- 需要逆序访问时使用rbegin/rend
- 结合算法库使用能提升效率
- 大数据量处理时考虑内存预分配
10. 与其他容器对比
特性 | vector | deque | list |
---|---|---|---|
内存结构 | 单端连续 | 多段连续 | 非连续 |
随机访问 | O(1) | O(1) | O(n) |
头部插入 | O(n) | O(1) | O(1) |
尾部插入 | O(1) | O(1) | O(1) |
中间插入 | O(n) | O(n) | O(1) |
迭代器类型 | 随机访问 | 随机访问 | 双向 |
内存预分配 | 支持 | 支持 | 不支持 |
选型建议:
- 需要随机访问 → vector/deque
- 频繁中间插入 → list
- 内存敏感场景 → array
- 先进先出队列 → deque
11. 总结与展望
vector作为STL中最基础的容器,其高效的内存管理和便捷的操作接口使其成为C++开发者的首选容器。理解其底层实现机制对于编写高性能代码至关重要。关键要点总结:
- 动态扩展机制:理解capacity和size的关系
- 迭代器失效:掌握容器修改后的正确使用方式
- 内存优化:合理使用reserve和swap技巧
- 算法结合:善用STL算法提升开发效率
随着C++标准的演进,vector的功能也在不断增强。C++17引入的emplace系列方法、C++20的范围操作等新特性,都使得vector的使用更加高效和安全。建议持续关注STL的发展动态,及时掌握新特性的使用方法。