stl删除优化历史

本文探讨了C++中使用vector删除元素时的三个典型错误案例,包括内存安全问题、迭代器失效和end()计算的误导。通过实例解析,强调了正确处理vector操作和避免bug的重要性,以及推荐了使用remove和erase的正确方法。
摘要由CSDN通过智能技术生成

摘自:

https://www.cnblogs.com/skyofbitbit/p/3648841.html

例1:

#include <iostream>
#include <vector>
using namespace std;
void main( ) {
       vector<int> vectInt;
       int i;
       //     初始化vector容器
       for (i = 0; i < 5; i++ ) {
               vectInt.push_back( i );
       }
       //     以下代码是要删除所有值为4的元素
       vector<int>::iterator itVect = vectInt.begin();
       for ( ; itVect != vectInt.end();   ++itVect ) {
               if ( *itVect == 4 ) {
                     vectInt.erase( itVect );
               }
       }
       int iSize = vectInt.size();
       for (   i = 0 ; i < iSize; i++ )   {
                     cout << " i= " << i <<   ", " << vectInt[ i ] << endl;
       }
}

例1将导致程序程序down掉。因为vectInt.erase( itVect );调用后itVect之后的迭代器已无效了,所以当执行++itVect后,*itVect访问了非法内存。例1也是初学者最容易犯的错误,这个错误也 比较容易发现。

例2:

#include <iostream>
#include <vector>
using namespace std;
void main( ) {
       vector<int> vectInt;
       int i;
       //     初始化vector容器
       for ( i = 0; i < 5; i++ ) {
               vectInt.push_back( i );
               if ( 3 == i ) {
                     //       使3的元素有两个,并且相临。这非常关键,否则将发现不了bug。
                     //   具体解释见下。
                     vectInt.push_back( i );
               }
       }
       vector<int>::iterator itVect = vectInt.begin();
       vector<int>::iterator itVectEnd = vectInt.end(); //       防止for多重计算
       //     以下代码是要删除所有值为3的元素
       for ( ; itVect != itVectEnd; ++itVect ) {
               if ( *itVect == 3 ) {
                     itVect = vectInt.erase( itVect );
               }
       }
       int iSize = vectInt.size();
       for (   i = 0 ; i < iSize; i++ )   {
                     cout << " i= " << i <<   ", " << vectInt[ i ] << endl;
       }

例2会导致不能把vectInt中所有为3的元素删除掉。因为第一次删除成功时,itVect = vectInt.erase( itVect );itVect为指向3之后的位置,之后再执行++itVect,itVect就掉过了被删除元素3之后的元素3,导致只删除了一个为3的元素,这个 bug比较隐蔽,因为如果不是两个均为3的元素相临,就将很难捕捉到这个bug,程序有可能在一段时间运行良好,但如碰到容器中两值相同的元素相临,则程 序就要出问题。

例3:

#include <iostream>
#include <vector>
using namespace std;
void main( ) {
       vector<int> vectInt( 5 );
       int i;
       vectInt[ 0 ] = 0;
       vectInt[ 1 ] = 1;
       vectInt[ 2 ] = 2;
       vectInt[ 3 ] = 3;
       vectInt[ 4 ] = 4; //     替换为 vectInt[ 4 ] = 3;试试
       vector<int>::iterator itVect = vectInt.begin();
       vector<int>::iterator itVectEnd = vectInt.end(); //       防止for多重计算
       //     以下代码是要删除所有值为3的元素
       for ( ; itVect != itVectEnd; ) {
               if ( *itVect == 3 ) {
                     itVect = vectInt.erase( itVect );
               }
               else {
                     ++itVect;
               }
       }
       int iSize = vectInt.size();
       for (   i = 0 ; i < iSize; i++ )   {
                     cout << " i= " << i <<   ", " << vectInt[ i ] << endl;
       }
}

例3,对于本例你可能要说程序没有任何问题,解决了上面的两个bug,程序也运行正常。但且慢,你把 “vectInt[ 4 ] = 4;” 这一行改为 “vectInt[ 4 ] = 3;”试试,一运行,程序当掉,访问非法内存!你疑惑不解:从程序看不出bug,而且我还把vectInt.end()放在外面计算以防止for多重计算,提高效率。

问题就出在vectInt.end(), 由于在for循环中要删除元素,则vectInt.end()是会变化的,所以不能在for循环外计算,而是每删除一次都要重新计算,所以应放在 for循环内。那你要问,为什么把 “vectInt[ 4 ] = 4;” 这一行改为 “vectInt[ 4 ] = 3;”程序就会当掉,而不改程序就很正常呢?这就跟vector的实现机制有关了。

为了效率,vector会申请超过需要的内存保存数据,删除数据时也不会把多余的内存删除。删除后多余内存并没有清0。然后itVect再执行++itVect,因为此时*itVect等于4,所以继续循环, 这时itVect 等于“新的end”但不等于“原来的end”(它即为itVectEnd),所以继续,因为 *itVect访问的是只读内存得到的值为4,不等于3,故不删除,然后执行++itVect此时itVect等于itVectEnd退出循环。从上面过程可以看出,程序多循环了一次(删除几次,就要多循环几次),但程序正常运行。

当把 “vectInt[ 4 ] = 4;” 这一行改为 “vectInt[ 4 ] = 3;”时, 最后的判断是需要删除的, 但是此时因为*itVect访问的是只读内存不能删除(或者重复删除了),所以程序down掉。

综上,我们知道当要删除的值在容器末尾时,会导致程序删除非法内存,程down当掉;即使程序正常运行,也是for循环多执行了等于删除个数的循环。所以把 vectInt.end()放在for循环外面执行,完全是错误的。除非你能保证for 循环中不会改变容器的大小,否则都应该对容器的值在for循环中计算。

例4:

正确的方法

#include <iostream>
#include <vector>
using namespace std;
void main( ) {
       vector<int> vectInt;
       int i;
       for (   i = 0; i < 5; i++ ) {
               vectInt.push_back( i );
               if ( 3 == i ) {
                     //       使3的元素有两个,并且相临。
                     vectInt.push_back( i );
               }
       }
       vector<int>::iterator itVect = vectInt.begin();

       //     以下代码是要删除所有值为3的元素

       for ( ; itVect != vectInt.end();   ) {   // 删除 ++itVect{

               if ( *itVect == 3 ) {

                     itVect = vectInt.erase( itVect );
               }
               else { 

                     ++itVect;
               }
       }
       //     把vectInt.size()放在for循环中

       for (   i = 0 ; i < vectInt.size(); i++ )   {
                     cout << " i= " << i <<   ", " << vectInt[ i ] << endl;
       }

运行结果为:
i= 0, 0
i= 1, 1
i= 2, 2
i= 3, 4

在通用算法中的 remove(包括remove_if)函数,并不真正从容器中删除元素,而是“应被删除的元素”被其后的“未被删除的元素”覆盖。返回值ForwardIterator指向经移除后的最后元素的下一位置。如vector{0,1,2,3,3,4},执行remove(),希望移除所有值为3的元素,结果为{0,1,2,4,3,4},返回值 ForwardIterator指向第5个元素, 移除值为3的元素。移除后3被其后的4替代,最后两位元素为残余数据。

例 5:

void main() {
       vector<int> vectInt;
       int i;
       for (i = 0; i < 5; i++ ) {
               vectInt.push_back( i );
               if ( 3 == i ) {
                     vectInt.push_back( i );
               }
       }
       remove( vectInt.begin(), vectInt.end(), 3 );
       cout << " after deleted , size = " << vectInt.size() << endl;
       for ( i = 0; i < vectInt.size();; i++ ) {
               cout << "i = " << i << " , " << vectInt[i] << endl;
       }
}
运行结果为:
after deleted , size = 6 // 从这行可以看出,移除后容器的大小没变
i = 0 , 0
i = 1 , 1
i = 2 , 2
i = 3 , 4 //   从这行可以看出:“应被删除的元素”3 被其后的“未被删除的元素”4覆盖
i = 4 , 3
i = 5 , 4     

所以要彻底删除还应该把后面的残余数据删除掉,这可以通过调用容器的成员函数erase()做到。

例 6:

void main() {
       vector<int> vectInt;
       int i;
       for (i = 0; i < 5; i++ ) {
               vectInt.push_back( i );
               if ( 3 == i ) {
                     vectInt.push_back( i );
               }
       }
       vectInt.erase( remove( vectInt.begin(), vectInt.end(), 3 ), vectInt.end() );
       cout << " after deleted , size = " << vectInt.size() << endl;
       for ( i = 0; i < vectInt.size();; i++ ) {
               cout << "i = " << i << " , " << vectInt[i] << endl;
       }
}
运行结果为:
after deleted , size = 4 // 从这行可以看出,删除后容器的大小变化了
i = 0 , 0
i = 1 , 1
i = 2 , 2
i = 3 , 4

从结果可以看出,所有值为3的元素确实被删除了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值