关于 迭代器 的看法
1. 如何获取一个迭代器(iterator)
2. 迭代器本身能够执行什么操作
3. 哪些表 ADT 方法需要迭代器作为参数
iterator begin(): 返回一个适当的迭代器,表示容器中的第一项
iterator end(): 返回一个适当的迭代器,表示容器的尾端(终端)标记(即容器中最后一项之后的位置)
end 方法有些不同寻常,因为它返回一个“越界”的迭代器
迭代器可以用!=和==进行比较,
itr++ 和 ++itr :将迭代器推进到下一个位置。
前缀形式和后缀形式都可以使用的。
*itr :返回对存储在迭代器 itr 的位置上对象的引用。
所返回的引用可以允许修改,也可以不允许修改。
itr1 = itr2 :若 itr1 和 itr2 指向同一个位置则返回 true ,否则返回 false。
itr1 !=itr2 :若 itr1 和 itr2 指向不同的位置则返回 true ,否则返回 false。
iterator insert (iterator pos,const object &x): 把 x 添加到表中由迭代器 pos 所给定的位置之前的位置上。
这是对 list (但不是对 vector )的常数时间的操作。返回值是指向被插入项的位置的一个迭代器。
iterator erase (iterator pos) : 删除由迭代器所给定的位置上的对象。这是对 list (但不是对 vector )的常数时间的操作。返回值是调用之前 pos 的后继元素所在的位置。
该操作使 pos 失效,现在它是多余的了,因为它正在指向的容器项已经被删除。
iterator erase (iterator start,iterator end): 删除从位置 start 开始直到(但不包括)位置 end ,为止的所有的项。
注意:整个表可以通过调用 c.erase(c.begin(),c.end()) 而被删除。
使用迭代器隔项删除(vector 或 list)中的项。
对 list 是高效的,但对 vector 则不是。
template <typename container> void removeEverOtherItem(container & lst){ auto itr = lst.begin(); //itr 是一个container::iterator while(itr!=lst.end){ itr = lst.erase(itr); if(itr!=lst.end()) ++itr; } }
第4 行上 auto 的使用时C++11的特色,它使我们避免了更长的类型 Container::iterator 。
线性例程
因为运行时间是按照输入大小的相同增长因子增长的。
const_iterators
*itr 的结果不只是迭代器正在指向的项的值,而且还有该项本身。
这个特点使得迭代器非常强大,不过也带来了一些副作用,
假设我们想要把一个集合中的所有项改成一个特定的值。
下面例程对于 vector 和 list 都是高效的并且是以线性时间运行的,它是编写一般的,类型无关代码的完美示例。
template <typename container,typename object> void change(container & c,const object & newvalue){ typename container::iterator itr = c.begin(); while(itr!=c.end()) *itr++ = newvalue; }
要想看清潜在的问题,假设 container c 是使用常量引用调用被传递到例程的。
这意味着我们不想对 c 做任何的改变,而编译器将通过禁止调用 c 的任何修改函数来保证这一点。
考虑下列代码,它打印由一些整数构成的 list 而且还试图对 list 进行隐蔽的修改:
void print(const list<int> & lst,ostream & out = cout){ typename Container::iterator itr = lst.begin(); while(itr!=lst.end()){ out << *itr << endl; *itr = 0; ++itr; } }
上面的代码太容易被绕开了。
STL 提供的解决方案是:
每一个集合容器不仅包含一个内嵌类型 iterator,而且还有一个内嵌类型的 const_iterator。在 iterator 和 const_iterator 之间的主要区别在于,
对于 cost_iterator ,operator * 返回一个常量引用,因而对于 const_iterator 的 *itr 不能出现在赋值语句的左边。
作为 C++11 中的自由函数,begin 和 end 的加入通过添加语言特色 auto 和 decltype 而成为可能。
template <typename container> auto begin(container & c)-> decltype(c.begin()){ return c.begin(); } template <typename container> auto begin(const container & c)->decltype(c.begin()){ return c.begin(); }
begin 的返回类型经推导是 c.begin() 的类型。
打印任意容器
template <typename container> void print(const container &c,ostream &out = cout){ if(c.empty()) out << "(empty)"; else{ auto itr = begin(c); out << "[" << *itr++; while(itr!=end(c)) out << "," << *itr++; out << "]" << endl; } }
vector 的实现
#include <algorithm> template <typename object> class vector { public: explicit vector(int initsize = 0):thesize{initsize}, thecapacity{initsize+spare_capacity} { objects = new object[thecapacity]; } vector (const vector & rhs):thesize{rhs.thesize}, thecapacity{ rhs.thecapacity },objects{ nullptr } { objects = new object[thecapacity]; for (int k = 0; k < thesize;++k) object[k] = rhs.objects[k]; } vector & operator=(const vector &rhs) { vector copy = rhs; std::swap(*this, copy); return *this; } ~vector(){ delete[] objects; } vector(vector && rhs):thesize{rhs.thesize}, thecapacity{rhs.capacity},object{rhs.objects} { rhs.objects = nullptr; rhs.thesize = 0; rhs.thecapacity = 0; } vector & operator=(vector && rhs) { std::swap(thesize, rhs.thesize); std::swap(thecapacity, rhs.thecapacity); std::swap(objects, rhs.objects); return *this; } void resize(int newsize) { if(newsize>thecapacity) reserve(newsize * 2); thesize = newsize; } void reserve(int newcapacity) { if(newcapacity<thesize) return; object *newarray = new object[newcapacity]; for (int k = 0; k < thesize;++k) newarraay[k] = std::move(object[k]); thecapacity = newcapacity; std::swap(objects, newarray); delete[] newarray; } object & operate[](int index) { return objects[index]; } const object & operator[](int index)const { return objects[index]; } bool empty() const { reutrn size() == 0; } int capacity() const { return thecapacity; } void push_back(const object & x) { if(thesize==thecapacity) reserve(2 * thecapacity + 1); objects[thesize++] = x; } void push_back(object && x) { if(thesize == thecapacity) reserve(2 * thecapacity + 1); objects[thesize++] = std::move(x); } void pop_back() { --thesize; } const object & back () const { return object[thesize - 1]; } typedef object *iterator; typedef const object *const_iterator; iterator begin() { return &objects[0]; } const_iterator begin() const { return &objects[0]; } iterator end() { return &objects[size()]; } const_iterator end()const { return &objects[size()]; } static const int spare_capacity = 16; private: int thesize; int thecapacity; object *objects; };
在 C++中,指针变量拥有我们对 iterator 期望的所有相同的运算符。
指针变量可以被复制和比较;* 运算符得到指针所指向的对象,而最特别的是,
当++用于指针变量时,指针变量则指向顺序存储的下一个对象:
如果指针正在指向数组内部,则增加指针的位置使它指向数组的下一元素。
list 的实现
4个类
1.list 类本身,它包含连接到表两端的链,表的大小,以及一些方法。
2.node 类,它很可能是一个私有的内嵌类。一个节点包含数据和指向前后两个节点的两个指针,以及一些适当的构造函数。
3.const_iterator 类,它抽象了位置的概念,而且是一个公有的内嵌类。该 const_iterator 存储一个指向“当前”节点的指针,并提供基本迭代器操作的实现,所有的操作,像=,==,!=和++等,均以重载运算符的形式出现。
4。iterator 类,它抽象了位置的概念,而且是一个公有的内嵌类,与3.有相同的功能,但 operator* 返回的是所指项的引用,而不是对该项的常量引用。一个重要的技术问题是,iterator可以用在任何需要const_iterator的例程中,但反之不真。换句话说,iterator 是一个 const_iterator。
template <typename object> class list { private: struct node{ object data; node *prev; node *next; node(const object & d = object{ },node *p=nullptr, node *n = nullptr) :data{d},prev{p},next{n} { } node(object && d,node *p = nullptr,node *n = nullptr) :data{ std::move(d)},prev{p},next{n} { } }; public: class const_iterator{ public: const_iterator():current{ nullptr } { } const object & operator * ( )const{ return retrieve(); } const_iterator & operator++(){ current = current->next; return *this; } const_iterator operator++ (int){ const_iterator old = *this; ++(*this); return old; } bool operator == { const const_iterator & rhs }const { return current == rhs.current; } bool operator!=( const const_iterator & rhs) const{ return !(*this == rhs); } protected: node *current; object & retrieve() const{ return current->data; } const_iterator(node *p):current{p} {} friend class list<object>; }; class iterator : public const_iterator{ public: iterator() {} object & operator * () { return const_iterator::retrieve(); } const object & operator * () const{ return const_iterator::operator*(); } iterator & operator++(){ this->current = this->current->next; return *this; } iterator & operator++(int){ iterator old = *this; ++(*this); return old; } protected: iterator(node *p):const_iterator{p} {} friend class list<object>; }; public: list(){ init(); } ~list(){ clear(); delete head; delete tail; } list(const list & rhs){ init(); for(auto & x:rhs) push_back(x); } list & operator = (const list & rhs){ list copy = rhs; std::swap(*this, copy); return *this; } list(list && rhs) :thesize{rhs.thesize},head{rhs.head},tail{rhs.tail} { rhs.thesize = 0; rhs.head = nullptr; rhs.tail = nullptr; } list & operator =(list && rhs){ std::swap(thesize, rhs.thesize); std::swap(head, rhs.head); std::swap(tail, rhs.tail); return *this; } iterator begin(){ return {head->next}; } const_iterator begin()const{ return {head->next}; } iterator end(){ return {tail}; } const_iterator end()const{ return {tail}; } init size()const{ return thesize; } bool empty()const{ reutrn size() == 0; } void clear(){ while(!empty()) pop_front(); } object & front(){ return *begin(); } const object & front() const{ return *begin(); } object & back(){ return *--end(); } const object & back() const{ return *--end(); } void push_front(const object & x){ insert(begin(), x); } void push_front(object && x){ insert(begin(), std::move(x)); } void push_back(const object &x){ insert(end(), x); } void push_back(object && x){ insert(end(), std::move(x)); } void pop_front(){ erase(begin()); } void pop_back(){ erase(--end()); } iterator insert(iterator itr,const object & x){ node *p = itr.current; thesize++; return {p->prev = p->prev->next = new node{x, p->prev, p}}; } iterator insert(iterator itr,object && x){ node *p = itr.current; thesize++; return {p->prev = p->prev->next = new node{std::move(x), p->prev, p}}; } private: int thesize; node *head; node *tail; void init(){ thesize = 0; head = new node; tail = new node; head->next = tail; tail->prev = head; } };
本文来自博客园,作者:小白兔奶糖, 侵权必究!!!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?