接上一篇。
所以要彻底删除还应该把后面的残余数据删除掉,这可以通过调用容器的成员函数ERASE()做到。
从结果可以看出来,所有值为3的元素确实被删除了。
二、使用STL中通用算法或容器成员函数删除元素的方法
上一篇手工编写的FOR循环代码删除容器中元素的方法也有一些问题,如果判断条件特别复杂,又有循环判断的话,循环中间又有异常处理的话,++itVect的位置就要小心放置了,稍不留意就要出错,所以手工编写代码删除容器中元素的方法不太安全,代码重复,也不够优雅,要注意的地方很多。
对于这种情况,可以考虑使用STL中通用算法remove()和Remove_if()帮忙。而Remove()和Remove_if()这两个算法也有一个问题需要程序员特别小心,在通用算法中的remove(remove_if)函数并不真正从容器中删除元素,而是“应被删除元素”被其后的“未被删除的元素”覆盖。返回值ForwardIterator指向移除后的最后元素的下一个位置。
如Vector(0,1,2,3,3,4),执行Remove(),希望一处所有值为3的元素,结果为(0,1,2,4,3,4),返回值ForwardIterator指向第5个元素。
即:
0 1 2 3 3 4 移除前
0 1 2 4 3 4 移除后
移除值为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
例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
对于Vector容器存放其他比较复杂的对象,就可以用remove_if()加函数对象的方法。
例7:
- #include <iostream>
- #include <sstream>
- #include <string>
- #include <vector>
- #include <algorithm>
- #include <list>
- using namespace std;
- class CTest
- {
- public:
- CTest(const string& str, int iPrice):m_strName(str),m_iPrice(iPrice){}
- void vPrint(){ cout<<"name=" << m_strName << "price=" << m_iPrice <<endl;}
- private:
- string m_strName;
- int m_iPrice;
- // 由于两个函数对象要访问CTest类的Private成员,所以设为友员;
- friend class CStrFunc;
- friend class CIntFunc;
- };
- // 函数对象,根据string比较
- class CStrFunc
- {
- string m_str;
- public:
- CStrFunc(const string& str):m_str(str){ }
- bool operator() (const CTest& left) { return (m_str == left.m_strName)?true:false; }
- };
- // 函数对象,根据int比较
- class CIntFunc
- {
- int m_iPrice;
- public:
- CIntFunc(int iPrice):m_iPrice(iPrice){ }
- bool operator()(const CTest& left) { return (m_iPrice == left.m_iPrice)?true:false; }
- };
- void main()
- {
- vector<CTest> vectTest;
- int i;
- for(i=0;i<5;++i)
- {
- stringstream stream; // 流格式化符,把int转化为string;
- stream<<i;
- string str = stream.str();
- CTest dTest(str,i);
- vectTest.push_back(dTest);
- }
- for ( i = 0 ; i < vectTest.size(); i++ )
- {
- vectTest[ i ].vPrint();
- }
- // 删除所有m_strName = "3"的元素
- vectTest.erase( remove_if( vectTest.begin(), vectTest.end(), CStrFunc( "3" ) ), vectTest.end() );
- cout << "delete 3 after : " << endl;
- for ( i = 0 ; i < vectTest.size(); i++ )
- {
- vectTest[ i ].vPrint();
- }
- // 删除所有m_iPrice = 2的元素
- vectTest.erase( remove_if( vectTest.begin(), vectTest.end(), CIntFunc( 2 ) ),vectTest.end() );
- cout << "delete 2 after : " << endl;
- for ( i = 0 ; i < vectTest.size(); i++ )
- {
- vectTest[ i ].vPrint();
- }
- }
手工编写for循环代码删除STL序列式容器中元素的方法,使用STL中通用算法或容器成员函数删除元素的方法,两者之间的比较:
1. 前者代码重复。
2. 前者容易出错,不够清晰。
3. 效率:
0 1 2 3 2 5 6 7
0 1 3 2 5 6 7
0 1 3 5 6 7
用第一种方法删除所有值为2的元素
从上图可以看出,每删除一个元素,后面的所有元素都到往前移动一位,导致一次内存大搬迁。
0 1 2 3 2 5 6 7
0 1 3 2 5 6 6 7
0 1 3 5 6 7
用第二种方法删除所有值为2的元素
从上面可以看出,删除时元素2被后面元素覆盖,不会到元素移位和内存大搬迁,残余数据留到末尾一次全部删除,也不会导致内存大搬迁,所以后者的方法要比前者在效率上好很多。
1. 前者代码重复。
2. 前者容易出错,不够清晰。
3. 效率:
0 1 2 3 2 5 6 7
0 1 3 2 5 6 7
0 1 3 5 6 7
用第一种方法删除所有值为2的元素
从上图可以看出,每删除一个元素,后面的所有元素都到往前移动一位,导致一次内存大搬迁。
0 1 2 3 2 5 6 7
0 1 3 2 5 6 6 7
0 1 3 5 6 7
用第二种方法删除所有值为2的元素
从上面可以看出,删除时元素2被后面元素覆盖,不会到元素移位和内存大搬迁,残余数据留到末尾一次全部删除,也不会导致内存大搬迁,所以后者的方法要比前者在效率上好很多。