vector文档
使用STL的三个境界:能用,明理,能扩展 ,那么下面学习vector,我们也是按照这个方法去学习
要包头文件#include
// 默认构造函数,创建一个空的 vectorvector<int>v1;// 创建一个包含 4 个默认初始化元素(值为 0)的 vectorvector<int>v2(4);// 创建一个包含 4 个元素,每个元素初始化为 10 的 vectorvector<int>v3(4,10);// 使用迭代器范围(v3 的起始和结束迭代器)初始化 vector,v4 将包含 v3 的所有元素vector<int>v4(v3.begin(),v3.end());// 拷贝构造函数,创建一个 v2 的副本vector<int>v5(v2);// 使用初始化列表创建 vector,v6 将包含 1, 2, 3, 4, 5, 6, 7 这些元素vector<int>v6 ={ 1,2,3,4,5,6,7};// 使用 std::string 初始化string s1("12345");// 创建一个包含 "12345" 的字符串 s1// 使用 std::string 的迭代器初始化 std::vector // 这里每个 char 会隐式转换为其对应的 int(ASCII 值)vector<int>v3(s1.begin(),s1.end());// 使用字符串的迭代器初始化 vector
vector和string的区别:
std::vector:
- 不自动添加 \0:
- std::vector 只是一个通用的动态数组容器,它不会在末尾自动添加 \0。你需要手动管理字符串的结束标志。
- 当你需要将 std::vector 转换为 C 风格字符串时,你必须手动添加一个 \0。
std::string:
- 自动添加 \0:
- std::string 在内部管理一个以 \0 结尾的字符数组。这个空字符保证了字符串可以直接使用 C 风格字符串的函数。
- 当你创建或操作 std::string 对象时,\0 是自动添加和管理的,因此不需要手动处理。
voidtest_vector1(){ vector<int>v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for(size_t i =0;i <v.size();i++){ cout <<v[i]<<" ";}cout <<endl;}
看一下结果(push_back后面会将,顾名思义也就是尾插)
调试一下看看
at() 函数用于访问vector 中的元素,并进行边界检查。与 operator[] 不同,at() 会在访问越界时抛出 std::out_of_range 异常,因此它比 operator[] 更安全,但稍微有点性能开销。
#include // 引入输入输出流头文件#include // 引入 vector 容器头文件#include // 引入标准异常头文件usingnamespacestd;// 使用标准命名空间intmain(){ vector<int>vec ={ 10,20,30,40,50};// 初始化一个包含五个整数的 vector// 正常访问 vector 的元素try{ for(size_t i =0;i <vec.size();++i){ cout <<"Element at index "<<i <<" is "<<vec.at(i)<<endl;// 使用 at() 函数访问元素}}catch(constout_of_range&e){ cerr <<"Out of range error: "<<e.what()<<endl;// 捕捉并处理越界访问异常}// 尝试访问越界元素try{ cout <<"Element at index 10 is "<<vec.at(10)<<endl;// 访问索引为 10 的元素,这将抛出异常}catch(constout_of_range&e){ cerr <<"Out of range error————"<<e.what()<<endl;// 捕捉并处理越界访问异常}return0;// 返回 0 表示程序正常结束}
iterator的使用 | 接口说明 |
---|---|
begin +end(重点) | 获取第一个数据位置的iterator/const_iterator 获取最后一个数据的下一个位置 的iterator/const_iterator |
rbegin + rend | 获取最后一个数据位置的reverse_iterator 获取第一个数据前一个位置的 reverse_iterator |
迭代器访问+修改
vector<int>v1;// 定义一个空的 vector 容器v1.push_back(1);// 向容器中添加元素 1v1.push_back(2);// 向容器中添加元素 2v1.push_back(3);// 向容器中添加元素 3v1.push_back(4);// 向容器中添加元素 4vector<int>::iterator it =v1.begin();// 初始化迭代器,指向 vector 的起始位置// 使用 while 循环遍历 vector 的元素while(it !=v1.end()){ // 当迭代器未到达 vector 的末尾时继续循环*it -=10;// 将迭代器指向的元素值减去 10cout <<*it <<" ";// 打印当前元素值it++;// 迭代器指向下一个元素}cout <<endl;// 输出换行符
为什么 while (it != v1.end())不能用 it
因为在 C++ 中,标准库容器(如 std::vector)的迭代器支持比较操作,但通常是使用 != 而不是 < 来判断迭代器是否已经到达容器的末尾。!= 比较更加直观和符合语义。
使用 < 进行比较可能在某些情况下无效,因为并不是所有迭代器都支持 < 运算符。特别是,对于双向迭代器或更复杂的迭代器(如关联容器中的迭代器),它们不支持这种操作。
注意:vector
要用vector::指明是什么类型的迭代器。
voidvector_Traversal_test(){ vector<int>v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);// 范围forfor(autoe :v){ cout <<e <<" ";}cout <<endl;for(auto&e :v){ e +=10;cout <<e <<" ";}cout <<endl;//范围for实际上是使用迭代器来遍历容器的//把上面两个展开其实是下面两个。for(autoit =v.begin();it !=v.end();++it){ autoe =*it;// 通过迭代器获取当前元素cout <<e <<" ";}cout <<endl;for(autoit =v.begin();it !=v.end();++it){ auto&e =*it;// 通过迭代器获取当前元素的引用e +=10;// 修改元素的值cout <<e <<" ";}cout <<endl;}
size | 获取数据个数 | |
max_size | 容器所能容纳的最大元素数量 | |
capacity | 获取容量大小 | |
resize | 改变vector的size | |
reserve | 改变vector的capacity | |
empty | 判断是否为空 |
size() 函数返回当前 vector 中的元素个数。
max_size() 函数返回 vector 可以容纳的最大元素数量,这个数量通常是一个非常大的值,取决于系统限制和内存可用性。
capacity() 函数返回当前 vector 内部存储空间的容量,即在重新分配之前可以存储的元素数量。
reserve(n) 函数用于请求 vector 预留足够的存储空间,以容纳至少 n 个元素。这样做可以减少因为容器扩展而导致的重新分配操作,提高插入元素的效率。
如果已经确定vector中要存储元素大概个数,可以提前将空间设置足够,就可以避免边插入边扩容导致效率低下的问题了
voidTestVectorExpandOP(){ vector<int>v;size_t sz =v.capacity();v.reserve(100);// 提前将容量设置好,可以避免一遍插入一遍扩容cout <<"making bar grow:\n";for(inti =0;i <100;++i){ v.push_back(i);if(sz !=v.capacity()){ sz =v.capacity();cout <<"capacity changed: "<<sz <<'\n';}}}
resize() 函数用于改变 vector 的大小,即容器中元素的数量。它可以根据传入的参数 n,进行不同的操作:
empty() 函数检查 vector 是否为空,如果 vector 中没有元素,则返回 true,否则返回 false。
push_back | 尾插 |
---|---|
pop_back | 尾删 |
find | 查找(注意这个是算法模块实现,不是vector的成员接口) |
insert | 在pos位置之前插入数据 |
erase | 删除pos位置的数据 |
swap | 交换两个vector的数据空间 |
assign | 用于将新值分配给向量的元素,替换当前内容,并修改向量的大小 |
vector<int>v;v.push_back(1);v.push_back(2);v.push_back(3);for(autoe :v){ cout <<e <<" ";}cout <<endl;vector<string>v1;v1.push_back(" we");v1.push_back(" all");v1.push_back(" love");v1.push_back(" C++");for(autoe :v1){ cout <<e;}cout <<endl;
voidtest_vector5(){ vector<int>v;v.push_back(1);v.push_back(2);v.push_back(3);for(autoe :v)cout <<e <<" ";cout <<endl;v.pop_back();// 尾删:3for(autoe :v)cout <<e <<" ";cout <<endl;v.pop_back();// 尾删:2for(autoe :v)cout <<e <<" ";cout <<endl;}
思考:为什么string、map、set、都有自己的find而vector和list没有?
为什么 string、map、set 提供 find 操作?
- std::string:
- std::string 是一个字符序列,提供 find 操作用于子串查找,这是字符串操作中非常常见的需求。
- 例如,查找某个子串在字符串中的位置。
- std::map 和 std::set:
- std::map 和 std::set 是关联容器,基于平衡二叉树(如红黑树)实现。
- find 操作在这些容器中是核心功能,因为它们的主要用途就是快速查找键。
- std::map 提供键值对的查找,而 std::set 提供唯一键的查找。
- 这些容器的查找操作效率是 O(log n)。
为什么 vector 和 list 不提供 find 操作?- std::vector:
- std::vector 是一个动态数组,主要用于顺序存储和访问。
- 查找操作的效率是 O(n),因为需要线性扫描整个数组。
- 提供 find 操作在效率上不占优势,因此没有直接提供。
- std::list:
- std::list 是一个双向链表,适用于频繁插入和删除操作。
- 查找操作的效率同样是 O(n),因为需要线性扫描链表。
- 和 vector 类似,提供 find 操作在效率上不占优势,因此没有直接提供。
#include voidtest_vector9(){ vector<int>v;v.push_back(9);v.push_back(9);v.push_back(6);vector<int>::iterator it =find(v.begin(),v.end(),6);if(it !=v.end()){ cout <<"找到啦"<<endl;cout <<*it <<endl;}}
voidtest_vector9(){ vector<int>v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(6);autoit1 =v.begin()+2;//在第三个位置插入v.insert(it1,100);for(autoe :v){ cout <<e <<" ";}//1 2 100 3 4 5 6cout <<endl;autoit2 =v.begin()+4;v.insert(it2,3,90);//在第五个位置插入3个90for(autoe :v){ cout <<e <<" ";}//1 2 100 3 90 90 90 4 5 6cout <<endl;vector<int>vec1 ={ 1,2,6};vector<int>vec2 ={ 3,4,5};autoit3 =vec1.begin()+2;// 在第三个位置插入元素vec1.insert(it3,vec2.begin(),vec2.end());// 在位置 it 处插入 vec2 的所有元素// 现在 vec1 = { 1, 2, 3, 4, 5, 6}}
注意:pos的类型都是iterator;
vector<int>v { 1,2,3,4,5};// 删除第三个元素autoit =v.erase(v.begin()+2);// v = { 1, 2, 4, 5}for(autoelem :v){ cout <<elem <<" ";}cout <<endl;// 删除第二个到第四个元素autofirst =v.begin()+1;autolast =v.begin()+4;v.erase(first,last);// v = { 1, 5}for(autoelem :v){ cout <<elem <<" ";}cout <<endl;//注意:[first,last) 是左闭右开的区间所以last可以取v.begin() + 4
迭代器失效问题我会放在vector模拟实现(下一篇文章)详细讲解。
std::vector 提供了一个成员函数 swap,用于交换两个 vector 对象的内容。这个操作可以快速地交换两个容器的元素,而不需要复制它们的内容。
vector<int>v1 { 1,2,3};vector<int>v2 { 4,5,6};// 使用 swap 交换两个 vector 的内容v1.swap(v2);cout <<"After swapping:\n";cout <<"v1: ";for(autoelem :v1){ cout <<elem <<" ";//4 5 6}cout <<"\nv2: ";for(autoelem :v2){ cout <<elem <<" ";//1 2 3}cout <<endl;
效果和注意事项
总之,std::vector 的 swap 函数是一个非常有用的工具,能够快速、高效地交换两个 vector 对象的内容,适合在需要优化内存使用或者重新组织数据时使用。
voiddemonstrate_assign(){ // 创建一个空的 vector vector<int>v;// 使用 assign(n, value) 方法赋值v.assign(5,10);// 用5个10替换当前内容cout <<"After v.assign(5, 10): ";for(inti :v){ cout <<i <<" ";// 输出: 10 10 10 10 10}cout <<endl;// 使用 assign(first, last) 方法赋值intarr[]={ 1,2,3,4,5};v.assign(arr,arr +3);// 用数组的前3个元素替换当前内容cout <<"After v.assign(arr, arr + 3): ";for(inti :v){ cout <<i <<" ";// 输出: 1 2 3}cout <<endl;// 使用 vector 的迭代器范围赋值vector<int>v2 ={ 7,8,9};v.assign(v2.begin(),v2.end());// 用v2的所有元素替换当前内容cout <<"After v.assign(v2.begin(), v2.end()): ";for(inti :v){ cout <<i <<" ";// 输出: 7 8 9}cout <<endl;}
📜 [ 声明 ] 由于作者水平有限,本文有错误和不准确之处在所难免,
本人也很想知道这些错误,恳望读者批评指正!