C++ Primer 学习笔记_29_STL实践与分析(3) --顺序容器的操作(下)

来源:互联网 发布:程序员谷歌插件 编辑:程序博客网 时间:2024/05/10 08:22


STL实践与分析

--顺序容器的操作()



六、访问元素

    如果容器非空,那么容器类型的frontback成员将返回容器的第一个和最后一个元素的引用。

【与beginend的对比:】

    1beginend返回容器类型的迭代器,而不是引用;

    2end返回容器最后一个元素的下一个位置的迭代器,而back返回容器的最后一个元素的引用!

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. /* 
  2. *必须保证该list容器非空! 
  3. *如果容器为空,则if语句内的所有操作都是未定义的! 
  4. */  
  5.    if (!iList.empty())  
  6.    {  
  7.        list<int>::reference bVal1 = *iList.begin();  
  8.        list<int>::reference bVal2 = iList.front();  
  9.   
  10.        list<int>::reference eVal1 = *--iList.end();  
  11.        list<int>::reference eVal2 = iList.back();  
  12.   
  13.        cout << "Begin:" << endl;  
  14.        cout << bVal1 << endl;  
  15.        cout << bVal2 << endl;  
  16.        cout << endl << "End:" << endl;  
  17.        cout << eVal1 << endl;  
  18.        cout << eVal2 << endl;  
  19.    }  



访问顺序容器内元素的操作

c.back()

返回容器c最后一个元素的引用,如果c为空,则该操作未定义

c.front()

返回容器c第一个元素的引用,如果c为空,则该操作未定义

c[n]

返回下标n的元素的引用,如果n<0n>=c.size(),则该操作未定义

只适用于vectordeque容器

c.at(n)

返回下标为n的元素的引用,如果下标越界,则该操作未定义

只适用于vectordeque容器

    使用下标运算的一个可选方案是使用at成员函数,虽然这个函数的行为和下标运算相似,但是如果程序给出的下标无效,at函数会抛出out_of_range异常。

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. vector<string> strVec;  
  2.   
  3. cout << strVec[0] << endl;      //run-time error  
  4. cout << strVec.at(0) << endl;   //throw out_of_range  

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. //P280 习题9.24  
  2. int main()  
  3. {  
  4.     vector<string> strVec;  
  5.     strVec.push_back("o(∩∩)o...");  
  6.     if (!strVec.empty())  
  7.     {  
  8.         cout << strVec.front() << endl;  
  9.         cout << *strVec.begin() << endl;  
  10.         string tmp = strVec.at(0);  
  11.         cout << tmp << endl;  
  12.         tmp = strVec[0];  
  13.         cout << tmp << endl;  
  14.     }  
  15. }  

七、删除元素

删除顺序容器内元素的操作

c.erase(p)

删除迭代器p所指向的元素

返回一个迭代器,它指向被删除元素后面的元素。如果p指向容器内的最后一个元素,则返回的迭代器指向容器的超出末端的下一位置。如果p本身就是指向超出末端的下一位置的迭代器,则该函数未定义

c.erase(b,e)

删除迭代器be所标记的范围内所有的元素

返回一个迭代器,它指向被删除元素段后面的元素如果e本身就是指向超出末端的下一位置的迭代器,则返回的迭代器也指向容器的超出末端的下一位置

c.clear()

删除容器c内的所有元素。返回void

c.pop_back()

删除容器c的最后一个元素。返回void。如果c为空容器,则该函数未定义

c.pop_front()

删除容器c的第一个元素。返回void。如果c为空容器,则该函数未定义

只适用于listdeque容器



1、删除第一个/最后一个元素

    pop_front操作通常与front操作配套使用,实现以栈的方式处理容器:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. while (!iDeq.empty())  
  2. {  
  3.     proccess(iDeq.front());  
  4.     iDeq.pop_front();  
  5. }  

【注意:】

    pop_frontpop_back函数的返回值并不是删除的元素值,而是void。要获取删除的元素值,则必须在删除元素之前调用frontback函数。


2、删除容器内的一个/一段元素

    erase的两种形式都返回一个迭代器,它指向被删除元素或元素段后面的元素。也就是说,如果元素j恰好紧跟在元素i后面,则将元素i从容器中删除后,删除操作返回指向j的迭代器。

    如同其他操作一样,erase操作也不会检查它的参数。程序员必须确保用作参数的迭代器或迭代器范围是有效的。因此,在删除元素之前,必须确保迭代器不是end迭代器,如果恰巧是end迭代器,则erase的操作未定义!

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. void printVec(const vector<string> &strVec)  
  2. {  
  3.     for (vector<string>::const_iterator iter = strVec.begin(); iter != strVec.end(); ++iter)  
  4.     {  
  5.         cout << *iter << endl;  
  6.     }  
  7. }  
  8. int main()  
  9. {  
  10. //  freopen("input","r",stdin);  
  11.     vector<string> strVec;  
  12.     string val;  
  13.     while (cin >> val)  
  14.     {  
  15.         strVec.push_back(val);  
  16.     }  
  17.     printVec(strVec);  
  18.   
  19.     string searchVal("Quasimodo");  
  20.     vector<string>::iterator iter = find(strVec.begin(),strVec.end(),searchVal);  
  21.   
  22.     if (iter != strVec.end())  
  23.     {  
  24.         strVec.erase(iter);  
  25.     }  
  26.     printVec(strVec);  
  27. }   

3、删除容器内的所有元素

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. strVec.clear();  
  2. strVec.erase(strVec.begin(),strVec.end());  

    同时,erase函数的迭代器版本也提供了删除部分元素的功能:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. string searchVal("Quasimodo");  
  2. vector<string>::iterator iter = find(strVec.begin(),strVec.end(),searchVal);  
  3. strVec.erase(strVec.begin(),iter);  //不会包含iter指向的元素  
  4. printVec(strVec);  

    如果删除时,两个迭代器指向的元素是同一个元素,则不会删除任何元素;如果两个迭代器指向的元素有一个或两个不存在,则会发生运行时错误:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. strVec.erase(strVec.begin(),strVec.begin());  
  2. printVec(strVec);  
  3.   
  4. strVec.erase(strVec.begin(),strVec.end()+1);  
  5. printVec(strVec);  

【小心地雷o(∩∩)o...P282

    erasepop_frontpop_back函数使指向被删除元素的所有迭代器失效对于vector容器,指向删除点后面的元素的迭代器通常也会失效。而对于deque容器,如果删除时不包含第一个元素或最后一个元素,那么该deque容器相关的所有迭代器都会失效

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. //P282 习题9.26  
  2. void printVecInt(const vector<int> &strVec)  
  3. {  
  4.     for (vector<int>::const_iterator iter = strVec.begin(); iter != strVec.end(); ++iter)  
  5.     {  
  6.         cout << *iter << '\t';  
  7.     }  
  8.     cout << endl;  
  9. }  
  10.   
  11. void printlistInt(const list<int> &strVec)  
  12. {  
  13.     for (list<int>::const_iterator iter = strVec.begin(); iter != strVec.end(); ++iter)  
  14.     {  
  15.         cout << *iter << '\t';  
  16.     }  
  17.     cout << endl;  
  18. }  
  19.   
  20. int main()  
  21. {  
  22.     int ia[] = {0,1,1,2,3,5,8,13,21,55,89};  
  23.     vector<int> iVec(ia,ia+sizeof(ia)/sizeof(*ia));  
  24.     list<int> iList(ia,ia+sizeof(ia)/sizeof(*ia));  
  25. //    printlistInt(iList);  
  26. //    printVecInt(iVec);  
  27.   
  28.     for (vector<int>::iterator iter = iVec.begin(); iter != iVec.end(); ++iter)  
  29.     {  
  30.         if (!(*iter % 2))  
  31.         {  
  32.             iter =  iVec.erase(iter);  
  33.             -- iter;  
  34.         }  
  35.     }  
  36.     printVecInt(iVec);  
  37.   
  38.     for (list<int>::iterator iter = iList.begin(); iter != iList.end(); ++iter)  
  39.     {  
  40.         if (*iter % 2)  
  41.         {  
  42.             iter = iList.erase(iter);  
  43.             -- iter;  
  44.         }  
  45.     }  
  46.     printlistInt(iList);  
  47. }  

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. //习题9.27  
  2. int main()  
  3. {  
  4. //  freopen("input","r",stdin);  
  5.     list<string> strList;  
  6.     string val;  
  7.   
  8.     while (cin >> val)  
  9.     {  
  10.         strList.push_back(val);  
  11.     }  
  12.   
  13.     string searchVal("dream");  
  14.     for (list<string>::iterator iter = strList.begin(); iter != strList.end(); ++iter)  
  15.     {  
  16.         if (*iter == searchVal)  
  17.         {  
  18.             strList.erase(iter);  
  19.             --iter;  
  20.         }  
  21.     }  
  22.     for (list<string>::iterator iter = strList.begin(); iter != strList.end(); ++iter)  
  23.     {  
  24.         cout << *iter << '\t';  
  25.     }  
  26.     cout << endl;  
  27. }  

八、赋值与swap

顺序容器的赋值与swap操作

c1= c2

删除容器c1的所有元素,然后将c2的元素复制给c1

c1c2的类型(包括容器类型和元素类型)必须相同

c.assign(b,e)

重新设置c的元素:将迭代器be标记的范围内所有的元素复制到c中。be必须不是指向c中元素的迭代器

c.assign(n,t)

将容器c重新设置为存储n个值为t的元素

c1.swap(c2)

交换内容:调用完该函数后,c1中存放的是c2原来的元素,c2中存放的则是c1原来的元素。

c1c2的类型必须相同。

该函数的执行速度通常要比将c2复制到c1的操作快


    与赋值相关的操作符都作用于整个容器。除了swap外,其他操作都可以通过eraseinsert来替代。赋值操作符首先删除其左操作数容器的所有元素,然后将右操作数容器的所有元素插入到左边容器中:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. vec1 = vec2;  
  2. //等效于  
  3. vec1.erase(vec1.begin(),vec1.end());  
  4. vec1.insert(vec1.begin(),vec2.begin(),vec2.end());  

    尽管赋值前两个容器的长度可能不相等,但是赋值后两个容器的长度都等于右边容器的长度!

【小心地雷:】

   赋值和assign操作使左操作容器的所有迭代器失效,swap操作则不会使迭代器失效。完成swap操作后,尽管被交换的元素已经存放在另一容器中,但迭代器仍然指向相同的元素


1、使用assign

    1)带有一对迭代器参数的assign操作允许我们将一个容器的元素赋给另一个不同类型的容器。但是两种容器类型与元素类型必须相互兼容!

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. sList.assign(sVec.begin(),sVec.end());  

    2assign运算的第二个版本需要一个整型数值和一个元素值做参数,它将容器重置为存储指定数量的元素,并且每个元素的值都为指定值

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. sList.assign(10,"o(∩∩)o...");  

2、使用swap操作以节省删除元素的成本

    swap操作实现交换两个容器内所有元素的功能。要交换的容器的类型必须匹配:操作数必须是相同类型的容器,而且所存储的元素类型也必须相同。调用了swap函数后,右操作数原来存储的元素被存放在左操作数中,反之亦然。

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. void printListStr(const list<string> &sList)  
  2. {  
  3.     for (list<string>::const_iterator iter = sList.begin(); iter != sList.end(); ++iter)  
  4.     {  
  5.         cout << *iter << endl;  
  6.     }  
  7. }  
  8.   
  9. int main()  
  10. {  
  11.     list<string> sList1(3,"o(∩∩)o...");  
  12.     list<string> sList2(4,"(*^__^*)");  
  13.   
  14.     cout << "sList1:" << endl;  
  15.     printListStr(sList1);  
  16.     cout << "sList2:" << endl;  
  17.     printListStr(sList2);  
  18.   
  19.     sList1.swap(sList2);  
  20.     cout << "sList1:" << endl;  
  21.     printListStr(sList1);  
  22.     cout << "sList2:" << endl;  
  23.     printListStr(sList2);  
  24. }  

    关于swap的一个重要问题在于:该操作不会删除或插入任何元素,而且保证在常量时间内实现交换由于容器内没有移动任何元素,因此迭代器不会失效

    没有移动元素这个事实意味着迭代器不会失效。它们指向同一元素,就像没作swap运算之前一样。虽然,swap运算后,这些元素已经被存储在不同的容器之中了。例如,在做swap运算之前,有一个迭代器iter指向svec1[3]字符串;实现swap运算后,该迭代器则指向svec2[3]字符串(这是同一个字符串,只是存储在不同的容器之中而已)

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. vector<string> sVec1(4,"o(∩∩)o...");  
  2. vector<string> sVec2(3,"(*^__^*)");  
  3.   
  4. vector<string>::iterator iter = sVec1.end() - 1;  
  5. cout << *iter << endl;  
  6. sVec1.swap(sVec2);  
  7. cout << *iter << endl;  

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. //P284 习题9.28  
  2. void printListStr(const list<string> &sList)  
  3. {  
  4.     for (list<string>::const_iterator iter = sList.begin(); iter != sList.end(); ++iter)  
  5.     {  
  6.         cout << *iter << endl;  
  7.     }  
  8. }  

0 0
原创粉丝点击