c++ vector 与 迭代器
c++迭代器,可以理解成 指针的泛化。
迭代器与指针:迭代器(Iterator)是指针(pointer)的泛化,提供了对对象的间接访问。迭代器针对容器,而指针类型针对数组。
迭代器与模板:模板使得算法独立于存储的数据类型,即任何数据类型都可以使用该程序设计。而迭代器使得算法独立于使用的容器类型,即任何容器类型都可以使用该通用方法
原文链接:https://blog.csdn.net/baidu_28446365/article/details/89066288
========================
https://juejin.cn/post/6996883632400891935
c++ vector
简介
- vector 是顺序容器的一种,vector 是可变长的动态数组(可存放任意类型),支持随机访问迭代器。所有 STL 算法都能对 vector 进行操作,要使用 vector,需要包含头文件 vector
- 优点
- 因其拥有一段连续的内存空间,能非常好的支持随即存取,即[]操作符。
- 根据下标随机访问某个元素的时间是常数,在尾部添加一个元素的时间大多情况下也是常数,总体来说速度很快
- 缺点
- 若要表示的向量长度较长(需要为向量内部保存很多数),容易导致内存泄漏,而且效率会很低
- 在中间插入或删除元素时,因为要移动多个元素,因此速度较慢,平均花费的时间和容器中的元素个数成正比
函数列表
- Constructors 构造函数
- Operators 对vector中的元素赋值或比较
- assign() // 对vector中的元素赋值
- at() // 返回指定元素的位置
- back() // 返回最后一个元素
- begin() // 返回第一个元素的迭代器
- capacity() // 返回vector所能容纳的元素数量(在不重新分配内存的情况下)
- clear() // 清空所有元素
- empty() // 判断vector是否为空(空返回true)
- end() // 返回最末元素的迭代器(实指向最末元素的下一个位置)
- erase() // 删除指定元素
- front() // 返回第一个元素
- get_allocator() // 返回vector的内存分配器
- insert() // 插入元素到vector中
- max_size() // 返回vector所能容纳元素的最大数量(上限)
- pop_back() // 移除最后一个元素
- push_back() // 在vector最后添加一个元素
- rbegin() // 返回vector尾部的逆迭代器
- rend() // 返回vector起始的逆迭代器
- reserve() // 设置vector最小的元素容纳数量
- resize() // 改变vector元素数量的大小
- size() // 返回vector元素数量的大小
- seap() // 交换两个vector
函数详解
-
构造函数
- 语法:
- vector() // 无参构造函数,将容器初始化为空
- vector(int n) // 将容器初始化为有 n 个元素
- vector(int n, const T & val) // 假定元素类型为T,此构造函数将容器初始化为有 n 个元素,每个元素的值都是 val
- 案例
复制代码vector<int> v1(5, 6); // 构造了包含5个值为6的元素的vector
- 语法:
-
assign 函数
- 语法:
- void assign(input_iterator first, input_iterator last); // 将区间[first, last)的元素赋值到当前vector
- void assign(size_type num, const TYPE & val); // 赋num个值为val的元素到vector中
- assign函数会清除掉为vector赋值以前的内容
- 案例
复制代码#include <iostream> #include <vector> using namespace std; int main() { int a[5] = {1, 2, 3, 4, 5}; int b[2] = {1, 2}; vector<int> v1(a, a + 5), v2(b, b + 2), v3; vector<int>::iterator iter; cout<<"v1 = "; for(iter = v1.begin(); iter != v1.end(); iter ++) cout<<*iter<<" "; cout<<endl; cout<<"v2 = "; for(iter = v2.begin(); iter != v2.end(); iter ++) cout<<*iter<<" "; cout<<endl; v2 = v1; cout<<"v2 = "; for(iter = v2.begin(); iter != v2.end(); iter ++) cout<<*iter<<" "; cout<<endl; v2.assign(v1.begin(), v1.end()); cout<<"v2 = "; for(iter = v2.begin(); iter != v2.end(); iter ++) cout<<*iter<<" "; cout<<endl; v3.assign(7, 3); cout<<"v3 = "; for(iter = v3.begin(); iter != v3.end(); iter ++) cout<<*iter<<" "; cout<<endl; return 0; }
复制代码v1 = 1 2 3 4 5 v2 = 1 2 v2 = 1 2 3 4 5 v2 = 1 2 3 4 5 v3 = 3 3 3 3 3 3 3
- 语法:
-
at 函数
- 语法:
- TYPE at(size_type loc); // 返回当前vector指定位置loc的元素的引用
- at()函数比[]运算符更安全,at()不会让你访问到vector内越界的元素
- 案例1
复制代码// 此段代码越界访问,可能导致危险结果 #include <iostream> #include <vector> using namespace std; int main() { vector<int> v; v.assign(6, 6); for(int i = 0; i < 10; i ++) cout<<v[i]<<" "; cout<<endl; return 0; }
复制代码6 6 6 6 6 6 135137 0 0 0
- 案例2
复制代码// 改良后的代码使用at(), 在越界的时候抛出异常out_of_range #include <iostream> #include <vector> using namespace std; int main() { vector<int> v; v.assign(6, 6); for(int i = 0; i < 10; i ++) cout<<v.at(i)<<" "; cout<<endl; return 0; }
复制代码6 6 6 6 6 6 terminate called after throwing an instance of 'std::out_of_range' what(): vector::_M_range_check /usercode/script.sh: line 62: 13 Aborted $output - < "/usercode/inputFile"
- 语法:
-
erase 函数
- 语法:
- iterator erase(iterator loc); //删除指定位置loc的元素
- iterator erase(iterator first, iterator last); // 删除区间[first, last)的所有元素
- erase函数返回值是指向删除的最后一个元素的下一位置的迭代器
- 案例
复制代码// 创建一个vec,置入字母表的前十个字符 #include <iostream> #include <vector> using namespace std; int main() { vector<char> v; for(int i = 0; i < 10; ++ i) v.push_back(i + 65); int size = v.size(); vector<char>::iterator startIterator; vector<char>::iterator tempIterator; for(int i = 0; i < size; i ++) { startIterator = v.begin(); v.erase(startIterator); for(tempIterator = v.begin(); tempIterator != v.end(); tempIterator ++) cout<<*tempIterator; cout<<endl; } return 0; }
复制代码BCDEFGHIJ CDEFGHIJ DEFGHIJ EFGHIJ FGHIJ GHIJ HIJ IJ J
- 语法:
-
get_allocator 函数
- 语法:
- allocator_type get_allocator();
- get_allocator() 函数返回当前vector的内存分配器.在STL里面一般不会new或者alloc来分配内存,而是通过一个allocator对象的相关方法来分配
- 案例
复制代码vector3(3, 1, v2.get_allocator()); // 把v2的内存分配器作为一个参数参与构造v3. 这样,他们两个用一个内存分配器.
c++中的allocator类
概述
- 它用于将内存的分配和对象的构造分离开来. 它分配的内存是原始的、未构造的.
复制代码allocator<string> alloc; // 定义了一个可以分配string的allocator对象 auto const p = alloc.allocate(n); // 分配n个未初始化的string内存,即为n个空string分配了内存,分配的内存是原始的、未构造的
allocator用法
- allocator a // 定义了一个名为a的allocator对象,它可以为类型T的对象分配内存
- a.allocate(n) // 分配能保存n个类型为T的对象的内存
- a.deallocate(p, n) // 释放T*指针p地址开始的内存,这块内存保存了n个类型为T的对象,p必须是一个先前由allocate返回的指针,且n必须是p创建时所要求的大小,且在调用该函数之前必须销毁在这片内存上创建的对象,这是因为在创建的过程中我们分配的是最原始的内存,所以在释放内存的时候也只能严格释放这片最原始的内存
- a.construct(p, args) // p必须是一个类型为T*的指针,指向一片原始内存,arg将被传递给类型为T的构造函数,用来在p指向的原始内存上构建对象
- a.destory(p) // p为T*类型的指针,用于对p指向的对象执行析构函数
详解
-
allocate用于分配原始内存
- 正如前面说到,allocate出来的内存是最原始的,未构造的内存. 它的construct成员函数接受一个指针和零个或多个额外的参数,在给定位置构造对象, 额外的参数是用于初始化构造对象的
复制代码auto q = p; // q指向最后构造的元素之后的位置 alloc.construct(q++); // *q为空字符串 alloc.construct(q++, 10, 'c'); // *q为cccccccccc alloc.construct(q++, "hi"); // *q为hi
- 用完对象后,必须对这种构造的对象调用destory销毁,它接受一个指针,对指向的对象执行析构函数
复制代码while(q != p) alloc.destory(--q);
循环开始处,q是指向最后构造的元素之后的一个位置,调用destory之前我们先对q进行递减操作,所以第一次调用destory销毁的是最后一个元素,依次执行销毁操作直到q和p相等. 我们只能对真正构造了的元素进行destory操作,一旦元素被销毁,就可以重新使用这部分内存来保存其他string或归还给系统,释放内存通过调用deallocate完成
复制代码alloc.deallocate(p, n)
其中p不能为空,必须指向allocate分配的内存,而且大小参数n也必须与调用allocate分配内存时提供的大小参数相等
- 语法:
-
insert 函数
- 语法:
- iterator insert(iterator loc, const TYPE & val); // 在指定位置loc前插入值为val的元素,返回指向这个元素的迭代器
- void insert(iterator loc, size_type num, const TYPE & val); // 在指定位置loc前插入num个数值为val的元素
- void insert(iterator loc, input_iterator first, input_iterator last); // 在指定位置loc前插入区间[first, last)的所有元素
- 案例
复制代码// 创建一个vec,置入字母表的前十个字符 #include <iostream> #include <vector> using namespace std; int main() { vector<char> v; for(int i = 0; i < 10; ++ i) v.push_back(i + 65); // 插入4个C到vector中 vector<char>::iterator theIterator = v.begin(); v.insert(theIterator, 4, 'C'); // 显示vector中的内容 for(theIterator = v.begin(); theIterator != v.end(); theIterator ++) cout<<*theIterator; cout<<endl; return 0; }
复制代码CCCCABCDEFGHIJ
-
max_size 函数
- 语法:
- size_type max_size();
- max_size() 函数返回当前vector所能容纳元素数量的最大值(注:包括可重新分配内存)
- 语法:
-
pop_back() 函数
- 语法:
- void pop_back(); // 删除当前vector最末的一个元素
- 案例
复制代码#include <iostream> #include <vector> using namespace std; int main() { vector<char> v; for(int i = 0; i < 10; ++ i) v.push_back(i + 65); int size = v.size(); vector<char>::iterator theIterator; for(int i = 0; i < size; i ++){ v.pop_back(); for(theIterator = v.begin(); theIterator != v.end(); theIterator ++) cout<<*theIterator; cout<<endl; } return 0; }
复制代码ABCDEFGHI ABCDEFGH ABCDEFG ABCDEF ABCDE ABCD ABC AB A
- 语法:
-
rbegin 函数
- 语法:
- reverse_iterator rbegin(); // 返回指向当前vector末尾的逆迭代器(实际指向末尾的下一位置,而其实际内容为末尾元素的值)
- 案例
复制代码#include <iostream> #include <vector> using namespace std; int main() { vector<int> v; for(int i = 0; i <= 5; i ++) v.push_back(i); vector<int>::reverse_iterator pos; pos = v.rbegin(); for(int i = 0; i <= 5; i ++) { cout<<*pos<<" "; pos++; } return 0; }
复制代码5 4 3 2 1 0
- 语法:
-
rend 函数
- 语法:
- reverse_iterator rend(); // 返回指向当前vector起始位置的逆迭代器
- 案例
复制代码#include <iostream> #include <vector> using namespace std; int main() { vector<int> v; for(int i = 0; i <= 5; i ++) v.push_back(i); vector<int>::reverse_iterator pos; pos = v.rend(); for(int i = 0; i <= 5; i ++) { pos--; cout<<*pos<<" "; } return 0; }
复制代码0 1 2 3 4 5
- 语法:
-
reserve 函数
- 语法:
- void reserve(size_type size); // 设置为当前vector预留至少共容纳size个元素的空间(注:实际空间可能大于size)
- 语法:
-
resize 函数
- 语法:
- void resize(size_type size, TYPE val); // 改变当前vector的大小为size,且对新创建的元素赋值val
- 语法:
-
resize 与 reserve 的区别
- reserve 是容器预留空间,但并不真正创建元素对象,在创建对象之前,不能引用容器内的元素,因此当加入新的元素时,需要用push_back()/insert() 函数
- resize 是改变容器的大小,并且创建对象,因此,调用这个函数之后,就可以引用容器内的对象了,因此当加入新的对象时,用operator[]操作符,或者用迭代器来引用元素对象,
- resize就是重新分配大小,reserve就是预留一定的空间
- 附:reverse 与 resize 接口源码
复制代码void resize(size_type new_size){ resize(new_size, T());} void resize(size_type new_size, const & x){ if(new_size < oldsize) erase(oldbegin + new_size, oldend); // erase区间范围以外的数据,确保区间以外的数据无效 else insert(oldend, new size - oldsize, x); // 填补区间范围内空缺的数据,确保区间内的数据有效
- 案例
复制代码#include <iostream> #include <vector> using namespace std; int main() { vector<int> v; for(int i = 1; i <= 3; i ++) v.push_back(i); v.resize(5, 8); //多出的两个空间都初始化为8 for(int i = 0; i < v.size(); i ++) // resize 与 reverse并不会删除原先的元素以释放空间 cout<<v[i]; cout<<endl; v.reserve(7); // 新元素还没有构造 for(int i = 0; i < 7; i ++) cout<<v[i]<<" "; // 当i > 4。此时不能用[]访问元素 cout<<endl; cout<<v.size()<<endl; cout<<v.capacity()<<endl; return 0; }
复制代码12388 1 2 3 8 8 0 0 5 7
-
size 函数
- 语法:
- size_type size(); // 返回当前vector所容纳元素的数量
- 语法:
-
swap 函数
- 语法:
- void swap(vector & from); // 交换当前vector与vector from的元素
- 案例
复制代码#include <iostream> #include <vector> using namespace std; int main() { vector<int> v1, v2; for(int i = 1; i <= 3; i ++){ v1.push_back(i); v2.push_back(i); } v2.push_back(4); v2.push_back(5); v1.swap(v2); for(int j = 0; j < v1.size(); j ++) cout<<v1[j]<<" "; cout<<endl; for(int k = 0; k < v2.size(); k ++) cout<<v2[k]<<" "; cout<<endl; return 0; }
复制代码1 2 3 4 5 1 2 3
- 语法:
vector 用法
- vector 基本用法案例
#include <iostream>
#include <vector>
using namespace std;
template<class T>
void PrintVector(const vector <T> & v)
{
// 用于输出vector容器的全部元素的函数模板
typename vector <T>::const_iterator i; // const_iterator 类型的迭代器只能用于读不能进行重写
// typename 用来说明 vector<T>::const_iterator 是一个类型,在VS中不写也可以
for(i = v.begin(); i != v.end(); i ++)
cout<<*i<<" ";
cout<<endl;
}
int main()
{
int a[5] = {1, 2, 3, 4, 5};
vector<int> v(a, a + 5); // 将数组a的内容放入v
cout<<"1. "<<v.end() - v.begin()<<endl; // 两个随机迭代器可以相减
cout<<"2. "; PrintVector(v);
v.insert(v.begin() + 2, 13); // 在 begin() + 2 位置插入13
cout<<"3. "; PrintVector(v);
v.erase(v.begin() + 2); // 删除位于 begin() + 2 位置的元素
cout<<"4. "; PrintVector(v);
vector<int> v2(4, 100); // v2 有4个元素,都是100
v2.insert(v2.begin(), v.begin() + 1, v.begin() + 3); // 将v的一段插入v2开头
cout<<"5. v2: "; PrintVector(v2);
v.erase(v.begin() + 1, v.begin() + 3); // 删除v上的一个区间,即[2, 3)
cout<<"6. "; PrintVector(v);
return 0;
}
1. 5
2. 1 2 3 4 5
3. 1 2 13 3 4 5
4. 1 2 3 4 5
5. v2: 2 3 100 100 100 100
6. 1 4 5
- vector 还可以嵌套以形成可变长的二维数组
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<vector<int> > v(3); // v3有三个元素,每个元素都是vector容器
for(int i = 0; i < v.size(); i ++)
for(int j = 0; j < 4; j ++)
v[i].push_back(j);
for(int i = 0; i < v.size(); i ++){
for(int j = 0; j < v[i].size(); j ++)
cout<<v[i][j]<<" ";
cout<<endl;
}
return 0;
}
0 1 2 3
0 1 2 3
0 1 2 3
- 注:
- `vector<vector<int> > v(3);` 定义了一个 vector 容器,该容器中的每个元素都是一个 vector<int> 容器,即可以认为,v是一个二维数组,一共三行,每行都是一个可变长的一维数组
- 在 Dev C++ 中,上面写法中 int 后面的两个`>`之间需要有空格,否则有的编译器会把它们当作`>>`运算符,编译会出错
- vector 的元素不仅仅可以是int,double,string,还可以是结构体,但是要注意:结构体定义为全局的,否则会出错
- 案例
复制代码#include <iostream> #include <stdio.h> #include <algorithm> #include <vector> using namespace std; typedef struct rect{ int id; int length; int width; // 对于向量元素是结构体的。可在结构体内部定义比较函数,下面按照id,length,width升序排序 bool operator < (const rect & a) const { if(id != a.id) return id < a.id; else { if(length != a.length) return length < a.length; else return width < a.width; } } }Rect; int main() { vector<Rect> vec; Rect rect; rect.id = 1; rect.length = 2; rect.width = 3; vec.push_back(rect); vector<Rect>::iterator it = vec.begin(); cout<<(*it).id<<" "<<(*it).length<<" "<<(*it).width<<endl; return 0; }
复制代码1 2 3
链接:https://juejin.cn/post/6996883632400891935
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
========
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// 创建一个向量存储 int
vector<int> vec;
int i;
// 显示 vec 的原始大小
cout << "vector size = " << vec.size() << endl;
// 推入 5 个值到向量中
for(i = 0; i < 5; i++){
vec.push_back(i);
}
// 显示 vec 扩展后的大小
cout << "extended vector size = " << vec.size() << endl;
// 访问向量中的 5 个值
for(i = 0; i < 5; i++){
cout << "value of vec [" << i << "] = " << vec[i] << endl;
}
// 使用迭代器 iterator 访问值
vector<int>::iterator v = vec.begin();
while( v != vec.end()) {
cout << "value of v = " << *v << endl;
v++;
}
return 0;
}
===========================================
std::vector
的销毁
当一个 std::vector
对象超出其作用域或被显式删除时,其析构函数将被调用。std::vector
的析构函数负责释放分配的内存,并销毁存储的所有元素
#include <iostream>
#include <vector>
void createAndDestroyVector() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// vec 会在函数结束时自动销毁,释放内存
}
int main() {
createAndDestroyVector(); // 调用函数
// 当 createAndDestroyVector 返回时,vec 将被销毁
return 0;
}
=================================
以前一直想当然的以为vector 的clear()函数会保证释放vector的内存,今天网上一查资料发现完全不是我想象的那样子。
比如有如下代码:
1 tempObject obj1;
2 tempObject obj2;
3 vector<tempObject> tempVector;
4
5 tempVector.pushback(obj1);
6 tempVector.pushback(obj2);
tempVector.clear();
调用clear()函数只会调用tempObject的析构函数,从而释放掉obj1和obj2两个对象,不会释放vector所占用的内存。真正释放vector所占用的内存,要到vector对象离开作用域时,自动调用vector的析构函数释放内存。当然有一种强制释放内存的方法,比如针对上面的代码:
vector<tempObject>().swap(tempVector);
That will create an empty vector with no memory allocated and swap it with tempVector, effectively deallocating the memory.
需要注意的是:
vector 中的内建有内存管理,当 vector 离开它的生存期的时候,它的析构函数会把 vector 中的元素销毁,并释放它们所占用的空间,所以用 vector 一般不用显式释放 —— 不过,如果你 vector 中存放的是指针,那么当 vector 销毁时,那些指针指向的对象不会被销毁,那些内存不会被释放。
总结:
vector与deque不同,其内存占用空间只会增长,不会减小。比如你首先分配了10,000个字节,然后erase掉后面9,999个,则虽然有效元素只有一个,但是内存占用仍为10,000个。所有空间在vector析构时回收。
empty()是用来检测容器是否为空的,clear()可以清空所有元素。但是即使clear(),所占用的内存空间依然如故。如果你需要空间动态缩小,可以考虑使用deque。如果非要用vector,这里有一个办法:
在《effective STL》和其实很多C++文章中都有指明,用clear()无法保证内存回收。但是swap技法可以。具体方法如下所示:
vector<int> nums;
nums.push_back(1);nums.push_back(1);nums.push_back(2);nums.push_back(2);
vector<int>().swap(nums); //或者nums.swap(vector<int>());
vector<int>().swap(nums); 或者如下所示 加一对大括号都可以,意思一样的:
{
std::vector<int> tmp = nums;
nums.swap(tmp);
}
加一对大括号是可以让tmp退出{}的时候自动析构
swap技法就是通过交换函数swap(),使得vector离开其自身的作用域,从而强制释放vector所占的内存空间
Reference
[1]http://stackoverflow.com/questions/10464992/c-delete-vector-objects-free-memory
[2]关于vector的内存释放.http://blog.csdn.net/shenshenjin/article/details/6003425
=========
具体迭代器的实现可参考《STL源码剖析》
https://www.cnblogs.com/hchacha/p/7300657.html
https://users.cs.northwestern.edu/~riesbeck/programming/c++/stl-iterator-define.html
参考:
https://users.cs.northwestern.edu/~riesbeck/programming/c++/stl-iterator-define.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!