文档章节

C++(17)STL实践与分析之再谈迭代器

Simon丶Ma
 Simon丶Ma
发布于 2016/04/14 14:28
字数 5116
阅读 12
收藏 0

STL实践与分析

--再谈迭代器【上】



引言:

另外三种迭代器类型:

    1插入迭代器:这类迭代器与容器绑定在一起,实现在容器中插入元素的功能。

    2iostream迭代器:这类迭代器可以与输入与输出流绑定在一起,用于迭代遍历所关联的IO流。

    3反向迭代器:这类迭代器实现向后遍历,而不是向前遍历,所有的容器都定义了自己的reverse_iterator类型,由rbeginrend成员函数返回。

上述迭代器都在iterator头文件中定义。


一、插入迭代器

    前面曾经提到的back_inserter函数是一种插入器,插入器适宜用迭代器适配器,其带有一个容器参数,并生成一个迭代器,用于在指定容器中插入元素。通过插入迭代器赋值时,迭代器将会插入一个新的元素。

C++提供了三种插入迭代器,其差别在于插入元素的位置不同:

   1)back_inserter:创建使用push_back实现插入的迭代器。

   2)front_inserter使用push_front实现插入

   3)inserter:使用insert实现插入操作。出了所关联的容器外,inserter还带有第二个实参:指向插入起始位置的迭代器


1front_inserter需要使用push_front

    front_inserter的操作类似于back_inserter:该函数创建一个迭代器,调用它所关联的基础容器的push_front成员函数代替赋值操作。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. deque<int> iDeq;  
  2. front_inserter(iDeq) = 1;   //OK  
  3.   
  4. vector<int> iVec;  
  5. front_inserter(iVec) = 1;   //Error  
  6. back_inserter(iVec) = 1;    //OK  

    只有当容器提供 push_front操作,才能使用front_inserter。在vector或其他没有push_front运算的容器上使用front_inserter,将产生错误。


2inserter将产生在指定位置实现插入的迭代器

     inserter适配器提供更普通的插入形式。这种适配器带有两个实参:所关联的容器和指示起始插入位置的迭代器

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. list<int>::iterator it = find(iList.begin(),iList.end(),42);  
  2. /* 
  3. *在创建 inserter 时,应指明新元素在何处插入。 
  4. *inserter 函数总是在它的迭代器实参所标明的位置前面插入新元素。 
  5. */  
  6. replace_copy(iVec.begin(),iVec.end(),inserter(iList,it),0,100);  

3inserter+容器的begin组合与 front_inserter的区别:

    在使用front_inserter,元素始终在容器的第一个元素前面插入。而使用inserter,元素则在指定位置前面插入。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //理解下面输出的区别  
  2. list<int> ilst,ilst1,ilst2;  
  3.   
  4. for (list<int>::size_type index = 0; index != 4; ++index)  
  5. {  
  6.     ilst.push_front(index);  
  7. }  
  8.   
  9. copy(ilst.begin(),ilst.end(),front_inserter(ilst1));  
  10. copy(ilst.begin(),ilst.end(),inserter(ilst2,ilst2.begin()));  
  11.   
  12. cout << "iList1: " << endl;  
  13. for (list<int>::iterator iter = ilst1.begin(); iter != ilst1.end(); ++iter)  
  14. {  
  15.     cout << *iter << '\t';  
  16. }  
  17. cout << endl << "iList2: " << endl;  
  18. for (list<int>::iterator iter = ilst2.begin(); iter != ilst2.end(); ++iter)  
  19. {  
  20.     cout << *iter << '\t';  
  21. }  
  22. cout << endl;  

【小心地雷:】

    front_inserter的使用将导致元素以相反的次序出现在目标对象中!

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //P349 习题11.14  
  2. void printList(const list<int> &iList)  
  3. {  
  4.     for (list<int>::const_iterator iter = iList.begin();iter != iList.end(); ++iter)  
  5.     {  
  6.         cout << *iter << '\t';  
  7.     }  
  8.     cout << endl;  
  9. }  
  10.   
  11. int main()  
  12. {  
  13.     list<int> iList;  
  14.     list<int> iList1,iList2,iList3;  
  15.   
  16.     for (list<int>::size_type i = 0;i != 10; ++i)  
  17.     {  
  18.         iList.push_back(i);  
  19.     }  
  20.     cout << "iList: " << endl;  
  21.     printList(iList);  
  22.   
  23.     replace_copy(iList.begin(),iList.end(),front_inserter(iList1),0,100);  
  24.     cout << "iList1: " << endl;  
  25.     printList(iList1);  
  26.   
  27.     replace_copy(iList.begin(),iList.end(),inserter(iList2,iList2.begin()),0,100);  
  28.     cout << "iList2:" << endl;  
  29.     printList(iList2);  
  30.   
  31.     replace_copy(iList.begin(),iList.end(),back_inserter(iList3),0,100);  
  32.     cout << "iList3:" << endl;  
  33.     printList(iList3);  
  34. }  

二、iostream迭代器

      虽然iostream类型不是容器,但是标准库同样提供了iostream对象上使用的迭代器:istream_iterator用于读取输入流,而ostream_iterator用于写输出流。这些迭代器将它们所对应的流视为特定类型的元素序列。使用流迭代器时,可以用泛型算法从流对象中读取数据或写数据到流对象中。

iostream迭代器的构造函数

istream_iterator<T>in(strm);

创建从输入流strm读取T类型对象istream_iterator对象

istream_iterator<T>in;

istream_iterator对象的超出末端迭代器

ostream_iterator<T>in(strm);

创建T类型的对象写入到输出流strmostream_iterator对象

ostream_iterator<T>in(strm,delim);

创建将T类型的对象写入到输出流strmostream_iterator对象,再写入过程中使用delim作为元素的分隔符。delimit是以空字符结束的字符数组。

     流迭代器只定义了最基本的迭代器操作:自增、解引用和赋值。此外,可比较两个istream迭代器是否相等(或不等)ostream迭代器则不提供比较运算

istream_iterator的操作

it1== it2

it1!= it2

比较两个istream_iterator对象是否相等/不等。迭代器必须读取的是相同的类型。如果两个迭代器都是end值,则它们相等。对于两个都不指向流结束符位置的迭代器,如果它们使用同一个输入流构造,则它们也相等

*it

返回从流中读取的值

it-> mem

(*it).mem的同义词,返回从流中读取的对象的mem成员

++it

it++

通过使用元素类型提供>>操作符从输入流中获取下一个元素值,使迭代器向前移动。通常,前缀版本使迭代器再流中向前移动,并返回对加1后的迭代器的引用。而后缀版本使迭代器在流中向前移动后,返回原值



1、流迭代器的定义

    流迭代器都是类模板:任何已定义输入操作符(>>操作符)的类型都可以定义stream_iterator。类似地,任何已定义输出操作符(<<操作符)的类型也可定义ostream_iterator

    在创建流迭代器时,必须指定迭代器所读写的对象类型:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. istream_iterator<int> cin_it(cin);  
  2. istream_iterator<int> end_of_stream;  
  3.   
  4. ofstream outfile;  
  5. ostream_iterator<Sales_item> cout_it(outfile," ");  

【注意:】

    ostream_iterator对象必须与特定的流绑定在一起。在创建istream_iterator,可直接将它绑定到一个流上。另一种方法是在创建时不提供实参,则该迭代器指向超出末端位置。ostream_iterator不提供超出末端迭代器

    在创建ostream_iterator对象时,可提供第二个(可选的)实参,指定将元素写入输出流时使用的分隔符。分隔符必须是C风格字符串。因为它是C风格字符串,所以必须以空字符结束;否则,其行为将是未定义的。


2istream_iterator对象上的操作

    构造与流绑定在一起的istream_iterator对象时,将对迭代器定位,以便第一次对该迭代器进行解引用时即可从流中读取第一个值。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. vector<int> iVec;  
  2. istream_iterator<int> in_iter(cin);  
  3. istream_iterator<int> eof;  
  4.   
  5. while (in_iter != eof)  
  6. {  
  7.     iVec.push_back(*in_iter++);  
  8. }  

    while循环从cin中读取 int型数据,并将读入的内容保存在iVec中。每次循环都检查in_iter是否为 eof。其中eof迭代器定义为空的istream_iterator对象,用作结束迭代器。绑在流上的迭代器在遇到文件结束或某个错误时(如输入的是字符),将等于结束迭代器的值。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //可以这样重写该程序  
  2. istream_iterator<int> in_iter(cin);  
  3. istream_iterator<int> eof;  
  4.   
  5. vector<int> iVec(in_iter,eof);  

这里,用一对标记元素范围的迭代器构造vector对象。这些迭代器是istream_iterator对象,这就意味着这段范围的元素是通过读取所关联的流来获得的


STL实践与分析

--再谈迭代器【中】



二、iostream迭代【续】

3ostream_iterator对象和ostream_iterator对象的使用

     可以使用ostream_iterator对象将一个值序列写入流中,其操作过程与使用迭代器将一组值逐个赋值给容器中的元素相同:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ostream_iterator<string> out_iter(cout,"\n");  
  2. istream_iterator<string> in_iter(cin),eof;  
  3.   
  4. while (in_iter != eof)  
  5. {  
  6.     *out_iter++ = *in_iter++;  
  7. }  

读取cin,并将每个读入的值依次写入到cout中不同的行中。


4、在类类型上使用istream_iterator

     提供了输入操作符>>任何类型都可以创建istream_iterator对象。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. istream_iterator<Sales_item> item_iter(cin),eof;  
  2. Sales_item sum;  
  3. sum = *item_iter ++;  
  4.   
  5. while (item_iter != eof)  
  6. {  
  7.     if (item_iter -> same_isbn(sum))  
  8.     {  
  9.         sum += *item_iter;  
  10.     }  
  11.     else  
  12.     {  
  13.         cout << sum << endl;  
  14.         sum = *item_iter;  
  15.     }  
  16.     ++ item_iter;  
  17. }  
  18. cout << sum << endl;  

5、流迭代器的限制

    1)不可能从ostream_iterator对象读入,也不可能写到istream_iterator对象中。

    2)一旦给ostream_iterator对象赋了一个值,写入就提交了。赋值后,没有办法再改变这个值。此外,ostream_iterator对象中每个不同的值都只能正好输出一次。

    3)ostream_iterator没有 ->操作符。


6、与算法一起使用流迭代器

    由于算法是基于迭代器操作实现的,而流迭代器定义了一些迭代器操作。由于流迭代器操作,因此,至少可在一些泛型算法上使用这类迭代器。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //从文件中读取一些数,再将读取的不重复的数写到标准输出  
  2. ifstream inFile("input");  
  3. istream_iterator<int> in_iter(inFile),eof;  
  4.   
  5. vector<int> iVec(in_iter,eof);  
  6. sort(iVec.begin(),iVec.end());  
  7.   
  8. ostream_iterator<int> out_iter(cout," ");  
  9. unique_copy(iVec.begin(),iVec.end(),out_iter);  

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //第二种方式输出  
  2. vector<int>::iterator end_unique = unique(iVec.begin(),iVec.end());  
  3. cout << endl;  
  4. for (vector<int>::iterator iter = iVec.begin(); iter != end_unique; ++iter)  
  5. {  
  6.     *out_iter ++ = *iter;  
  7. }  

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //P353 习题11.16  
  2. int main()  
  3. {  
  4.     ifstream inFile("input");  
  5.   
  6.     istream_iterator<string> in_iter(inFile),eof;  
  7.     ostream_iterator<string> out_iter(cout," ");  
  8.   
  9.     while (in_iter != eof)  
  10.     {  
  11.         *out_iter ++ = *in_iter ++;  
  12.     }  
  13. }  

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //习题11.17  
  2. int main()  
  3. {  
  4.     ifstream inFile("input");  
  5.   
  6.     istream_iterator<int> in_iter(inFile),eof;  
  7.     vector<int> iVec(in_iter,eof);  
  8.   
  9.     for (vector<int>::iterator iter = iVec.begin(); iter != iVec.end(); ++iter)  
  10.     {  
  11.         cout << *iter << endl;  
  12.     }  
  13. }  

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //习题11.18 本题集合了iostream迭代器,文件流  
  2. int main()  
  3. {  
  4.     istream_iterator<int> in_iter(cin),eof;  
  5.     ofstream outFile1("output1"),outFile2("output2");  
  6.   
  7.     ostream_iterator<int> out_iter1(outFile1," "),out_iter2(outFile2,"\n");  
  8.   
  9.     while (in_iter != eof)  
  10.     {  
  11.         if (*in_iter % 2)  
  12.         {  
  13.             *out_iter1 ++ = *in_iter ++;  
  14.         }  
  15.         else  
  16.         {  
  17.             *out_iter2 ++ = *in_iter ++;  
  18.         }  
  19.     }  
  20. }  

三、反向迭代器

    反向迭代器是一种反向遍历容器的迭代器:从最后一个元素到第一个元素遍历容器。反向迭代器将自增/自减的含义反过来了:对于反向迭代器,++运算将访问前一个元素,而—元素将访问下一个元素。

   容器定义的rbeginrend成员,分别返回指向容器尾元素和首元素前一位置的反向迭代器。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //逆序输出vector对象中的元素  
  2. vector<int> iVec;  
  3. for (vector<int>::size_type i = 0; i != 10; ++i)  
  4. {  
  5.     iVec.push_back(i);  
  6. }  
  7.   
  8. for (vector<int>::reverse_iterator r_iter = iVec.rbegin(); r_iter != iVec.rend(); ++r_iter)  
  9. {  
  10.     cout << *r_iter << endl;  
  11. }  

    虽然颠倒自增和自减这两个操作符的意义似乎容易使人迷惑,但是它让程序员可以透明地向前或向后处理容器。例如,为了以降序排列vector,只需向sort传递一对反向迭代器:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. sort(iVec.rbegin(),iVec.rend());  

    当然,如果不嫌麻烦,也可以这样:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. sort(iVec.begin(),iVec.end(),cmp);  

   cmp函数定义如下:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. bool cmp(int a,int b)  
  2. {  
  3.     return a > b;  
  4. }  

1、反向迭代器需要使用自减操作符

     从一个既支持--也支持++的迭代器就可以定义反向迭代器。毕竟,反向迭代器的目的是移动迭代器反向遍历序列。标准容器上的迭代器既支持自增运算,也支持自减运算。但是,流迭代器却不然,由于不能反向遍历流,因此流迭代器不能创建反向迭代器


2、反向迭代器与其他迭代器之间的关系

    输出 string对象line中的第一个单词。使用find可很简单地实现这个任务:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. string::iterator comma = find(line.begin(),line.end(),',');  
  2. cout << string(line.begin(),comma) << endl;  

如果要输出列表中的最后一个单词,可以使用反向迭代器:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. string::reverse_iterator rcomma = find(line.rbegin(),line.rend(),',');  
  2. //下面的输出结果可能不能满足你的要求...  
  3. cout << string(line.rbegin(),rcomma) << endl;  

如果line中的最后一个单词是Primer,那么,输出的结果为:remirP【是不是很不可思议?】见下图:


使用反向迭代器时,以逆序从后向前处理string对象。为了得到正确的输出,必须将反向迭代器line.rbegin()和 rcomma转换为从前向后移动的普通迭代器。其实没必要转换line.rbegin(),因为我们知道转换的结果必定是line.end()。只需调用所有反向迭代器类型都提供的成员函数base转换rcomma即可:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. cout << string(rcomma.base(),line.end()) << endl;  

    正如 line_rbegin()line.end()一样,rcommarcomma.base()也指向不同的元素。从技术上来说,设计普通迭代器与反向迭代器之间的关系是为了适应左闭合范围这个性质的,所以,[line.rbegin(),rcomma) [rcomma.base(),line.end()) 标记的是line中的相同元素。

    使用普通的迭代器对反向迭代器进行初始化或赋值时,所得到的迭代器并不是指向原迭代器所指向的元素!!!



STL实践与分析

--再谈迭代器【下】



三、反向迭代器【续:习题】

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //P355 习题11.19  
  2. int main()  
  3. {  
  4.     vector<int> iVec;  
  5.     for (vector<int>::size_type index = 0; index != 10; ++index)  
  6.     {  
  7.         iVec.push_back(index);  
  8.     }  
  9.   
  10.     for (vector<int>::reverse_iterator r_iter = iVec.rbegin();  
  11.             r_iter != iVec.rend(); ++r_iter)  
  12.     {  
  13.         cout << *r_iter << endl;  
  14.     }  
  15. }  
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //习题 11.20  
  2. int main()  
  3. {  
  4.     vector<int> iVec;  
  5.     for (vector<int>::size_type index = 0; index != 10; ++index)  
  6.     {  
  7.         iVec.push_back(index);  
  8.     }  
  9.   
  10.     for (vector<int>::iterator iter = iVec.end()-1;iter >= iVec.begin(); --iter)  
  11.     {  
  12.         cout << *iter << endl;  
  13.     }  
  14. }  

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //习题 11.21  
  2. int main()  
  3. {  
  4.     int ia[] = {0,1,2,3,4,5,6,0,7,8,9,0,10};  
  5.     vector<int> iVec(ia,ia + sizeof(ia)/sizeof(*ia));  
  6.   
  7.     vector<int>::reverse_iterator r_iter = find(iVec.rbegin(),iVec.rend(),0);  
  8.     if (r_iter != iVec.rend())  
  9.     {  
  10.         cout << "element after the last 0 : " << *(-- r_iter) << endl;  
  11.     }  
  12.     else  
  13.     {  
  14.         cout << "Not found!" << endl;  
  15.     }  
  16. }  

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //习题11.22  
  2. int main()  
  3. {  
  4.     int ia[] = {0,1,2,3,4,5,6,0,7,8};  
  5.     vector<int> iVec(ia,ia + sizeof(ia)/sizeof(*ia));  
  6.   
  7.     list<int> iList;  
  8.   
  9.     vector<int>::reverse_iterator rbeg = iVec.rbegin(),rend;  
  10.   
  11.     for (int i = 0; i != 3; ++i)  
  12.         ++ rbeg;  
  13.   
  14.     rend = rbeg;  
  15.     for (int i = 0; i != 5; ++i)  
  16.         ++rend;  
  17.   
  18.     copy(rbeg,rend,back_inserter(iList));  
  19.   
  20.     for (list<int>::iterator iter = iList.begin(); iter != iList.end(); ++iter)  
  21.     {  
  22.         cout << *iter << endl;  
  23.     }  
  24. }  

四、const迭代器

    算法要求用于指定范围的两个迭代器必须具有完全一样的类型。比如我们曾将使用的find_first_of算法:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. find_first_of(it, roster1.end(),roster2.begin(), roster2.end())  

     该函数调用的输入范围由it和调用roster1.end()返回的迭代器指定。算法要求用于指定范围的两个迭代器必须具有完全一样的类型。roster1.end()返回的迭代器依赖于roster1的类型。如果该容器是const对象,则返回的迭代器是const_iterator类型;否则,就是普通的iterator类型。在这个程序中,roster1不是 const对象,因而end返回的只是一个普通的迭代器。

    如果我们将it定义为const_iterator,那么find_first_of的调用将无法编译。用来指定范围的两个迭代器的类型不相同。it是 const_iterator类型的对象,rotser1.end()返回的则是一个iterator对象。


五、五种迭代器

    可以根据算法要求它的迭代器的提供什么类型的操作,对算法分类:

迭代器分类

输入迭代器

读,不能写:只支持自增运算

输出迭代器

写,不能读:只支持自增运算

前向迭代器

读和写:只支持自增运算

双向迭代器

读和写:支持自增、自减运算

随机访问迭代器

读和写:支持完整的迭代器算术运算



1、输入迭代器

可用于读取容器中的元素,但是不保证能够支持容器的写入操作。输入迭代器必须至少提供下列操作:

    1)相等和不等操作符(==,!=),比较两个迭代器。

    2)前置和后置的自增运算(++),使迭代器向前递进指向下一个元素。

    3)用于读取元素的解引用操作符(*),此操作符只能出现在赋值运算的右操作数上。

   4)箭头操作符(->),这是(*it).member的同义语,也就是说,对迭代器进行解引用来获取其所关联的对象的成员。

输入迭代器只能顺序使用;一旦输入迭代器自增了,就无法再用它检查之前的元素。要求在这个层次上提供支持的泛型算法包括find和 accumulate标准库istream_iterator类型输入迭代器。


2、输出迭代器

可以视为与输入迭代器功能互补的迭代器;输入迭代器可用于向容器写入元素,但不保证能支持读取容器的内容。输入迭代器要求:

    1)前置和后置的自增运算(++),使迭代器向前递进指向下一个元素。

    2)解引用操作符(*),引操作符只能出现在赋值运算的左操作数上。给解引用的输出迭代器赋值,将对该迭代器所指向的元素做写入操作。

     输出迭代器可以要求每个迭代器的值必须正好写入一次。使用输出迭代器时,对于指定的迭代器值应该使用一次*运算,而且只能用一次。输出迭代器一般用作算法的第三个实参,标记起始写入的位置。例如,copy算法使用一个输出迭代器作为它的第三个实参,将输入范围内的元素复制到输出迭代器指定的目标位置。标准库ostream_iterator类型输出迭代器


3、前向迭代器

     用于读写指定的容器。这类迭代器只会以一个方向遍历序列。前向迭代器支持输入迭代器和输出迭代器提供的所有操作,除此之外,还支持对同一个元素的多次读写。需要前向迭代器的泛型算法包括replace


4、双向迭代器

   从两个方向读写容器。出了提供前向迭代器的全部操作,还支持前置和后置的自减运算(--)。需要使用双向迭代器的泛型算法包括reverse所有标准库容器提供的迭代器都至少达到双向迭代器的要求。


5、随机访问迭代器

    提供在常量时间内访问容器任意位置的功能。不仅支持双向迭代器的所有功能,还支持下面的操作:

    1)关系操作符<<=>>=,比较两个迭代器的相对位置

    2)迭代器与整型数值n之间的加法和减法操作符++=-和 -=,结果是迭代器在容器中向前(或退回)n个元素。

    3)两个迭代器之间的减法操作符(--),得到两个迭代器间的距离

    4)下标操作符iter[n],这是*(iter+ n) 的同义词。

   需要随机访问迭代器的泛型算法包括sort算法。vectordequestring迭代器是随机访问迭代器,用作访问内置数组元素的指针也是随机访问迭代器

   除了输出迭代器,其他类别的迭代器形成了一个层次结构:需要低级类别迭代器的地方,可使用任意一种更高级的迭代器

【总结】

     mapset和 list类型提供双向迭代器,stringvector和 deque容器上定义的迭代器都是随机访问迭代器都是随机访问迭代器,用作访问内置数组元素的指针也是随机访问迭代器。istream_iterator是输入迭代器,ostream_iterator则是输出迭代器。


【关键概念:关联容器与算法】

    尽管map和 set类型提供双向迭代器,但关联容器只能使用算法的一个子集。问题在于:关联容器的键是const对象。因此,关联容器不能使用任何写序列元素的算法。只能使用与关联容器绑在一起的迭代器来提供用于读操作的实参。

    在处理算法时,最好将关联容器上的迭代器视为支持自减运算的输入迭代器,而不是完整的双向迭代器。


【重点理解】

    C++标准为所有泛型和算术算法的每一个迭代器形参指定了范围最小的迭代器种类。例如:find至少需要一个输入迭代器。replace函数至少需要一对前向迭代器。replace_copy函数的头两个迭代器必须至少是前向迭代器,第三个参数代表输出目标,必须至少是输出迭代器。

    对于每一个形参,迭代器必须保证最低功能。将支持更少功能的迭代器传递给函数是错误的;而传递更强功能的迭代器则没问题

  向算法传递无效的迭代器类别所引起的错误,无法保证会在编译时被捕获到

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //P358 习题11.26(d)  
  2.     vector<int> iVec1,iVec2;  
  3.     //...  
  4.     //解释下列语句错误的原因,以及编译器能否检测出这类错误?  
  5.     sort(iVec1.begin(),iVec2.end());  
本文借鉴:http://blog.csdn.net/column/details/zjf666.html?&page=4

© 著作权归作者所有

Simon丶Ma
粉丝 4
博文 134
码字总数 299850
作品 0
深圳
程序员
私信 提问
看完这 7 条,模拟 C++ 新功能只是一个小目标!

但是,即使你无法使用这些功能,也不一定要放弃它们的好处。至少不用放弃全部。 有一些方法可以使用代码中新功能的思路,更准确地传达你的意图。 当然,这些方法肯定不如使用新版本C++本身的...

CSDN资讯
2018/09/08
0
0
C++ STL编程轻松入门 5

2.2.3 第三版:唯美主义的杰作   事态的发展有时候总会趋向极端,这在那些唯美主义者当中犹是如此。首先声明,我并不是一个唯美主义者,提供第二版程序的改进版,完全是为了让你更深刻的感...

暖冰
2015/11/21
61
0
泛型编程与设计新思维

作者: 徐景周 转帖: http://www.vckbase.com/document/viewdoc/?id=955 前言 永远记住,编写代码的宗旨在于简单明了,不要使用语言中的冷僻特性,耍小聪明,重要的是编写你理解的代码,理解...

ValueError
2011/01/12
360
1
【转载】数据结构利器之私房STL

数据结构利器之私房STL 此系列的文章适合初学有意剖析STL和欲复习STL的同学们。 学过c++的同学相信都有或多或少接触过STL。STL不仅仅是c++中很好的编程工具(这个词可能有点歧义,用类库更恰...

悠米海
2012/12/02
205
0
CPP strings 与标准模板库

Leetcode 838 push dominos 解体思路 STL标准模板库 提供了一组表示容器、迭代器、函数对象和算法的模板。 Alex Stepanov & Meng Lee 一起开发了STL, 1994年第一版发布。STL不是面向对象的编...

Fayder
2018/07/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

反射

类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化来实现对这个类进行初始化 加载: 将class文件读入内存, 并为之创建一个Class对象; 任何类...

凹凸凸
47分钟前
4
0
jQuery与Ajax的应用

jQuery与Ajax的应用 Ajax Ajax 即“Asynchronous Javascript And XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术,异步交互,传输的数据为XML.是一种在无需重新加载...

cjy_lean
58分钟前
6
0
查漏补缺,JVM系列:(JVM内存组成及分配)

java内存组成介绍:堆(Heap)和非堆(Non-heap)内存 按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”...

小刀爱编程
今天
5
0
Java实现哈希表

Java实现哈希表 基本概念 哈希表:Hash Table,也称为散列表。在待存放的数据中定义一个关键字k,通过一个映射关系f,将k映射到一个地址中,这个地址称为散列地址。之后查找该记录时,不用再...

盒饭加鸡腿
今天
5
0
透彻讲解:并发编程的优缺点

一直以来并发编程对于刚入行的小白来说总是觉得高深莫测,于是乎,就诞生了想写点东西记录下,以提升理解和堆并发编程的认知。为什么需要用的并发?凡事总有好坏两面,之间的trade-off是什么...

李红欧巴
今天
32
1

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部