慎重选择删除元素的方法

总结本条款,一共有以下几点是需要牢记的:

1、要删除容器中有特定值的所有对象:

(1)、如果容器是vector、string或deque,则使用erase-remove习惯用法。

(2)、如果容器是list,则使用list::remove

(3)、如果容器是标准关联容器,则使用它的erase成员函数。


eg:  

假定你有一个标注的STL容器c,它包含int类型的整数:Container<int> c;

而你想删除c中所有值为1963的元素,对vector、string和deque则可以使用erase-remove、习惯用法:

	c.erase(remove(c.begin(),c.end(),1963),c.end()); //当c是vecotr、string或deque时,
							//erase-remove习惯用法是删除特定值的元素的最好办法

对list,这一办法同样适用,根据Effective STL 中指出,list成员函数remove更加有效:

	c.remove(1963);					//当c是list时,remove成员函数
							//是删除特定值的元素的最好办法

对于关联容器,解决问题的正确方法是调用erase:

	c.erase(1963);					//当c是标准关联容器时,
							//erase成员函数式删除特定值元素的最好办法。

2.要删除容器中满足特定判别式(条件)的所有对象:

(1)、如果容器是vector、string或deque,则使用erase-remove-if习惯用法。

(2)、如果容器是list,则使用list::remove_if。

(3)、如果容器是一个标准关联容器,则使用remove_copy_if和swap,或者写一个循环来遍历容器中的元素,记住当把迭     代器传给erase时,要对它进行后缀递增。

(4)、要在循环内部做某些(除了删除对象之外的)操作:

 如果容器是一个标准序列容器,则写一个循环来遍历容器中的元素,记住每次调用erase时,要用它的返回值更新迭代器。

 如果容器是一个标准关联容器,则写一个循环来遍历容器中的元素,记住当把迭代器传给erase时,要对迭代器做后缀递增。


eg:

继续上面的容器,现在让我们把问题稍微给一下。我们不再从c中删除所有等于特定值的元素,而是删除使下面的判别式(predicate)返回true的每一个对象:


bool badValue(int x);                                                         //返回x是否为“坏值”


对于序列容器(vector,string,deque和list),我们把每个对remove的调用换成调用remove_if就可以了:

	c.erase(remove_if(c.begin(), c.end(), badValue), c.end()); 			//当c是vector、string、或deque时,
											//这是删除使badValue返回true的对象的最好办法

	c.remove_if(badValue);  //当c是list时,这是删除使badValue返回true的对象的最好办法。

 
	AssocContainer<int> c;                                      //c现在是一个标准关联容器
	...
	AssocContainer<int> goodValues;                             //保存不被删除的值的临时容器
	remove_copy_if(c.begin(), c.end(),                          //把不被删除的值从c复制到goodValues中
    	 inserter(goodValues, goodValues.end()),
	 badValue);
	c.swap(goodValues);                                         //交换c和goodValues的内容

这种办法的缺点是需要复制所有不被删除的元素,而我们可能并不希望付出这么多的复制代价。


我们也可以直接从原始的容器中删除元素,从而降低代价。但是,因为关联容器没有提供类似remove_if的成员函数,所以,我们必须写一个循环来遍历c中的元素,并在遍历中删除元素。

从概念上讲,任务很简单。实际上,代码也很简单。不幸的是,所能立刻想到的代码很少恰好是能工作的代码。比如,下面是很多我们首先能想到的代码:

AssocContainer<int> c;

...

for(AssocContainer<int>::iterator i = c.begin(); i != c.end(); ++i)

{

if (badValue(*i))  c.erase(i);

}

可惜,这会导致不确定的行为。当容器中的一个元素被删除时,指向该元素的所有迭代器都将变得无效。一旦c.erase(i)返回,i就成为无效值。对于这个循环,可是一个坏消息。因为在erase返回后,还要通过for循环的++i部分被递增。


为了避免这个问题,我们要确保在调用erase之前,有一个迭代器指向c中的一个元素。

这样做的最简单的办法是。当调用时对i使用后缀递增:


让我们再进一步把问题改一下。现在我们不仅要删除使badValue返回true的元素,我们还想再每次元素被删除时,都向一个日志(log)文件中写一条信息。

对于关联容器,这非常简单,因为它不仅需要对刚才的循环做简单的修改:

	AssocContainer<int> c;
	...
	for(AssocContainer<int>::iterator i = c.begin(); 
		i != c.end();				//for循环的第三部分是空的,i在下面递增。
		/*什么也不做*/)
	{
		if (badValue(*i))  c.erase(i++);	//对坏值,把当前的i传给erase,递增i是副作用
		else ++i;				//对好值,则简单地递增
	}


对于vector、string和deque,我们必须采取不同的策略,尤其要利用erase的返回值。返回值正是我们所需要的:一旦erase完成,它是指向紧随被删除元素的下一个元素的有效迭代器。或者说,我们可以这样写:

		ofstream logFile;                               //要写入的日志文件					
		AssocContainer<int> c;
		...
		for(AssocContainer<int>::iterator i = c.begin(); 
			i != c.end(); 			//for循环的第三部分是空的,i在下面递增。
		 	/*什么也不做*/)
		{
			if (badValue(*i)) 
			{		 
				logFile << "Erasing" << *i << '\n';	 //写日志文件
				c.erase(i++);				//对坏值,把当前的i传给erase,递增i是副作用
			}
			else ++i; 					//对好值,则简单地递增
		}


或许你想知道对list应采取何种方式。就遍历和删除来说,

你可以把list当做vector/string/deque来对待,

也可以把它当做关联容器来对待。两种方式对list都是适用的。

一般的惯例是对list采用和vector、string和deque一样的方式。






  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值