C++入门自学Day8-- 初识Vector
往期内容回顾
String类的自实现
String类的使用(续)
String类(续)
String类(初识)
C++ Vector 全面解析:从 C 数组到现代动态容器
前言
在 C 语言中,数组是最常见的顺序存储结构,但它存在固定长度、不能自动扩容、缺少边界检查等缺点。C++ 标准库引入了 std::vector 动态数组容器,为我们提供了更安全、更灵活的顺序存储解决方案。
本文将从 C 语言数组的局限性出发,深入解析 C++ vector 的设计理念、用法和底层原理,并结合实例对比二者的异同,帮助你从“旧世界”平滑过渡到“现代 C++”。
一、C++ 中的 vector 容器介绍
1. 什么是 vector?
vector 是 C++ 标准库(STL)中提供的一个序列容器,属于动态数组。它能够在运行时动态地管理存储空间,支持自动扩容,能方便地存储和操作一组同类型的元素。
简单来说,vector 就像一个动态大小的数组,使用起来非常灵活。
2. 为什么使用 vector?
-
动态大小:普通数组大小固定,不能改变;vector 可以根据需要自动增长或缩小。
-
随机访问:支持像数组一样通过索引快速访问元素,时间复杂度为 O(1)。
-
丰富的接口:提供了插入、删除、排序、遍历等常用操作。
-
安全性高:相比裸数组,vector 自动管理内存,避免了很多内存泄漏和越界问题。
-
与算法库兼容:可以方便地与 STL 其他算法和容器配合使用。
3. vector 的基本用法示例
#include <iostream> #include <vector>int main() {// 创建一个空的int类型vectorstd::vector<int> v;// 添加元素v.push_back(10);v.push_back(20);v.push_back(30);// 访问元素for (size_t i = 0; i < v.size(); ++i) {std::cout << v[i] << " ";}std::cout << std::endl;// 使用范围for循环for (auto elem : v) {std::cout << elem << " ";}std::cout << std::endl;return 0; }
4. vector 的常用成员函数
函数 | 作用 |
---|---|
push_back(value) | 在末尾添加元素 |
pop_back() | 删除末尾元素 |
size() | 返回当前元素个数 |
empty() | 判断是否为空 |
clear() | 清空所有元素 |
operator[] | 通过索引访问元素(不做越界检查) |
at(index) | 通过索引访问元素(做越界检查) |
front() | 返回第一个元素 |
back() | 返回最后一个元素 |
insert(pos, value) | 在指定位置插入元素 |
erase(pos) | 删除指定位置的元素 |
resize(n) | 改变容器大小 |
reserve(n) | 预留空间,减少扩容次数 |
capacity() | 当前分配的空间大小 |
二、常用函数接口详解
1. 构造与初始化
#include <vector> #include <iostream> using namespace std;int main() {vector<int> v1; // 空 vectorvector<int> v2(5); // 5 个默认值(0)vector<int> v3(5, 42); // 5 个 42vector<int> v4 = {1, 2, 3}; // 列表初始化vector<int> v5(v4); // 拷贝构造 }
2. 容量相关函数
v.size(); // 当前元素个数 v.capacity(); // 已分配的容量 v.empty(); // 是否为空 v.reserve(100); // 预留容量(减少扩容次数) v.resize(10); // 改变元素个数,多余的补默认值// 查看容量的变化 void test5(){vector<int> v1;v1.assign({1,2,3,4,5,6,7,8,9});// int arr[] = {1,2,3,4,5,6,7,8,9};for(int i =10;i<100;i++){v1.push_back(i);if (i%10 == 0){cout<< v1.capacity()<<endl;}} }int main(){// test1();test5(); }
3. 元素访问
v[0]; // 不做边界检查 v.at(0); // 有边界检查,越界会抛出异常 v.front(); // 第一个元素 v.back(); // 最后一个元素 v.data(); // 返回底层数组指针
4. 修改操作
v.push_back(10); // 末尾添加 v.pop_back(); // 删除末尾 v.insert(v.begin()+1, 20); // 插入 v.erase(v.begin()+2); // 删除单个元素 v.clear(); // 清空void test5(){vector<int> v1;v1.assign({1,2,3,4,5,6,7,8,9});v1.insert(v1.begin(),10);v1.insert(v1.begin()+3,20);for(auto num:v1){cout<< num<<" ";}cout<<endl;v1.erase(v1.begin());v1.erase(v1.end()-1); //v1.end() 指向的是容器末尾的“下一个位置”,不指向任何有效元素for(auto num:v1){cout<< num<<" ";}cout<<endl; }int main(){// test1();test5(); }
输出描述:
10 1 2 20 3 4 5 6 7 8 9
1 2 20 3 4 5 6 7 8
5. 迭代器遍历
for (size_t i = 0; i < v.size(); ++i) cout << v[i] << " "; for (auto it = v.begin(); it != v.end(); ++it) cout << *it << " "; for (auto &x : v) cout << x << " "; // C++11 范围 forvoid test1(){vector<int> v1;v1.assign({1,2,3,4,5,6,7,8,9});//遍历修改数据for(int i = 0;i<v1.size();i++){cout<< v1[i]<<" ";}cout<<endl;/// for(auto num :v1){cout<< num<<" ";}cout<<endl;///vector<int>::iterator it ;for(it = v1.begin();it<v1.end(); it++){cout<<*it<<" ";}cout<<endl; }
注意:const iterator的用法
void Print_Vector(const vector<int>& v){vector<int>::const_iterator it = v.begin();while (it != v.end()){(*it) += 1;cout<< *it<<" ";it++;}cout<<endl; } void test3(){vector<int> v1;v1.assign({1,2,3,4,5,6,7,8,9});Print_Vector(v1); }
const iterator无法进行元素的修改
反向迭代器:reverse_iterator
void test4(){//const 迭代器无法修改内容vector<int> v1;v1.assign({1,2,3,4,5,6,7,8,9});vector<int>::reverse_iterator it = v1.rbegin();while (it != v1.rend()){cout<< *it<<" ";it++;}cout<<endl; }int main(){// test1();test4(); }
输出:
9 8 7 6 5 4 3 2 1
三、Vector 的底层原理
-
存储结构
内部维护一个连续内存区 + size(元素数量)+ capacity(已分配容量)。
-
扩容机制
当 size == capacity 时,分配更大的内存(通常 1.5~2 倍),拷贝旧数据,再释放旧内存。
-
时间复杂度
-
随机访问:O(1)
-
尾部插入/删除:均摊 O(1)
-
中间插入/删除:O(n)
-
-
异常安全
扩容时可能抛出异常(内存分配失败),但保证不泄漏资源。
四、与算法库的联动
注意我们可以发现其实vector的库中并没有提供find函数--查找指定元素的位置,但是在<algorithm>头文件中提供了find函数模版接口。
void test6(){vector<int> v1;v1.assign({1,2,3,4,5,6,7,8,9});vector<int>::iterator pos = find(v1.begin(),v1.end(),5);for(auto num:v1){cout<<num<<" ";}cout<<endl; }int main(){// test1();test6(); }
输出描述:
1 2 3 4 5 6 7 8 9
那么这里的find函数是如何实现的呢?-->利用函数模版进行实现的
template<class Inputiterator, class T> Inputiterator find(Inputiterator begin, Inputiterator end, T val){while (begin!=end){ if(*begin == val){return begin;}begin++;}}
这里的class Inputiterator指的就是我们的迭代器(指针),class T 就是寻找的变量类型。
五、与 C 数组的互操作
#include <vector> #include <cstring> using namespace std;int main() {vector<int> v = {1, 2, 3};int *arr = v.data(); // 获取 C 风格指针memcpy(arr, arr+1, sizeof(int) * 2); // C API 操作 }
六. 总结
vector 是 C++ 最常用的序列容器,灵活且高效。它结合了数组的快速访问和动态管理内存的优势,适合绝大多数存储需求。熟练掌握 vector,能够极大提高 C++ 编程的效率和代码质量。