2.使用STL中通用算法或容器成员函数删除元素的方法
以上手工编写for循环代码删除容器中元素的方法也有一些问题,如果判断条件特别复杂,又有循环判断的话,循环中间又有异常处理的话,++itVect的位置就要小心放置了,稍不留意就要出错。所以手工编写代码删除容器中元素的方法不太安全,代码重复,也不够优雅,要注意的地方很多。
对于这种情况,可以考虑使用STL中通用算法remvoe()和remove_if()帮忙。而remvoe()和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
所以要彻底删除还应该把后面的残余数据删除掉,这可以通过调用容器的成员函数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的元素确实被删除了。
对于vector容器存放其他比较复杂的对象,就可以用remove_if()加函数对象(Function Object)的方法。
如:
例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 clTest( str, i );
vectTest.push_back( clTest );
}
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被后面元素覆盖,不会到元素移位和内存大搬迁,残余数据留到末尾一次全部删除,也不会导致内存大搬迁,所以后者的方法要比前者在效率上好很多。