“指针”对所有C/C++的程序员来说,一点都不陌生。在接触到C语言中的malloc函数和C++中的new函数后,我们也知道这两个函数返回的都是一个指针,该指针指向我们所申请的一个“堆”。提到“堆”,就不得不想到“栈”,从C/C++程序设计的角度思考,“堆”和“栈”最大的区别是“栈”由系统自动分配并且自动回收,而“堆”则是由程序员手动申请,并且显示释放。如果程序员不显示释放“堆”,便会造成内存泄漏,内存泄漏的危害大家知道,严重时会导致系统崩溃。
既然“指针”的使用者一不小心就可能导致内存泄漏,那么我们如何能够使得指针的使用变得更安全呢?从C++面向对象的角度分析,我们有没有可能将“指针”封装起来,使得用户不直接接触指针,而使用一个封装后的对象来替代指针的操作呢?
答案是显然的,“智能指针”(smart pointer)正解决这类问题,尤其是在防止内存泄漏方面做得非常突出。
迭代器iterator就是一种智能指针,它对原始指针进行了封装,并且提供一些等价于原始指针的操作,做到既方便又安全。
简单来说就是提供一种方法,在不需要暴露某个容器的内部表现形式情况下,使之能依次访问该容器中的各个元素,这种设计思维在STL中得到了广泛的应用,是STL的关键所在,通过迭代器,容器和算法可以有机的粘合在一起,只要对算法给予不同的迭代器,就可以对不同容器进行相同的操作。
尽管我们可以使用下标来访问字符串中的字符或vector的元素,但更一般的机制是使用迭代器(iterator)。
所有的容器都支持迭代器,但仅少数几个支持下标操作。可以替代下标访问vector对象的元素。
合法的迭代器:
- 指示某个元素
- 指示最后一个元素的下一个位置
- 其它的迭代器都是不合法的。
- 使用迭代器
迭代器提供对一个容器中的对象的访问方法,并且定义了容器中对象的范围。迭代器就如同一个指针。事实上,C++的指针也是一种迭代器。但是,迭代器不仅仅是指针,因此你不能认为他们一定具有地址值。例如,一个数组索引,也可以认为是一种迭代器。
迭代器有各种不同的创建方法。程序可能把迭代器作为一个变量创建。一个STL容器类可能为了使用一个特定类型的数据而创建一个迭代器。作为指针,必须能够使用*操作符类获取数据。你还可以使用其他数学操作符如++。典型的,++操作符用来递增迭代器,以访问容器中的下一个对象。如果迭代器到达了容器中的最后一个元素的后面,则迭代器变成past-the-end值。使用一个past-the-end值得指针来访问对象是非法的,就好像使用NULL或为初始化的指针一样。
提示:
STL不保证可以从另一个迭代器来抵达一个迭代器。例如,当对一个集合中的对象排序时,如果你在不同的结构中指定了两个迭代器,第二个迭代器无法从第一个迭代器抵达,此时程序注定要失败。这是STL灵活性的一个代价。
每种容器类型都定义了自己的迭代器类型,如 vector:
vector<int>::iterator iter;
语句定义了一个名为 iter 的变量,它的数据类型是 vector<int> 定义的 iterator 类型。每个标准库容器类型都定义了一个名为 iterator 的成员,这里的 iterator 与迭代器实际类型的含义相同。
begin 和 end 操作
vector<int>::iterator iter = ivec.begin();
vector 迭代器的自增和解引用运算
*iter = 0;
解引用操作符返回迭代器当前所指向的元素。假设 iter 指向 vector 对象 ivec 的第一元素,那么 *iter 和 ivec[0] 就是指向同一个元素。上面这个语句的效果就是把这个元素的值赋为 0。
迭代器使用自增操作符向前移动迭代器指向容器中下一个元素。从逻辑上说,迭代器的自增操作和 int 型对象的自增操作类似。对 int 对象来说,操作结果就是把 int 型值“加 1”,而对迭代器对象则是把容器中的迭代器“向前移动一个位置”。因此,如果 iter 指向第一个元素,则 ++iter 指向第二个元素
由于 end 操作返回的迭代器不指向任何元素,因此不能对它进行解引用或自增操作。
迭代器的其他操作
迭代器应用的程序示例
使用迭代器下标、迭代器改变vector的内容
#include <iostream> #include <string> #include <vector> int print_int_vector(std::vector<int> ivec) { for(std::vector<int>::size_type ix =0, j = 0; ix != ivec.size(); ++ix, ++j) { std::cout<<ivec[ix]<<" "; //加空格! } std::cout<<std::endl; return 0; } int main() { std::vector<int> ivec(10, 68); // empty vector print_int_vector(ivec); // reset all the elements in ivec to 0 /* // 使用下标 for (std::vector<int>::size_type ix = 0; ix != ivec.size(); ++ix) { ivec[ix] = 0; } */ // equivalent loop using iterators to reset all the elements in ivec to 0 for (std::vector<int>::iterator iter = ivec.begin(); iter != ivec.end(); ++iter) *iter = 0; // set element to which iter refers to 0 print_int_vector(ivec); return 0; }
const_iterator
C++为每种容器类型定义了一种名为const_iterator的类型,该类型只能用于读取容器内的元素,但不能改变其值。
对const_iterator类型解引用,得到的是一个指向const对象的引用。
for (vector<string>::const_iterator iter = text.begin(); iter != text.end(); ++ iter){ cout << *iter << endl; //ok: print each element in text *iter = " "; // error: *iter is const }
const_iterator可以用于const或者非const容器(因为不能修改对象的值),但是const的iterator只能用于非const容器(只能修改唯一指向的值)。
不要把 const_iterator 对象与 const 的 iterator 对象混淆起来。声明一个 const 迭代器时,必须初始化迭代器。一旦被初始化后,就不能改变它的值
vector<int> nums(10); // nums is nonconst const vector<int>::iterator cit = nums.begin(); *cit = 1; // ok: cit can change its underlying element ++cit; // error: can't change the value of cit
总结
迭代器的算术操作
*iter = i; // set element to which iter refers to i ivec.push_back(i*2);
加上这句代码没问题,正确运行,但是,我们试图在for循环里面执行,即
{ *iter = i; // set element to which iter refers to i ivec.push_back(i*2); }
则会莫名其妙退出!
参考博客:
http://lib.csdn.net/article/cplusplus/25895
http://blog.csdn.net/jxh_123/article/details/30793397?utm_source=tuicool&utm_medium=referral
http://blog.csdn.net/xxin_w/article/details/20551939
http://www.cnblogs.com/besty/p/4039264.html
http://ceeji.net/blog/cpp-learn-iterator/
http://blog.csdn.net/zhanh1218/article/details/33340959
http://blog.csdn.net/u014800748/article/details/47401429
感谢The_Third_Wave