谨慎优化代码

       看了本人前面关于“STL容器的删除元素问题”的博客后,相信大家对erase()函数的作用都已经有一定了解了,再使用的时候也会有所注意了,那么我们再看看下面的代码:

vector<int> vec(5);

//插入数据
vec[0] = 0;
vec[1] = 1;
vec[2] = 2;
vec[3] = 3;
vec[4] = 4;

//过滤数据
vector<int>::iterator iter = vec.begin();
vector<int>::iterator itEnd = vec.end();
for (; iter != itEnd;)
{
	//删除值为3的数据
	if (3 == *iter)
	{
		iter = vec.erase(iter);
	}
	else
	{
		++iter;
	}
}

       上面的代码,乍一看完全没有问题,程序也运行正常,但如果将“vec[4] = 4”改为“vec[4] = 3"之后,一运行程序就会core掉。上面代码对于erase的使用是正确的,那为什么还会出现问题呢?
       在写C++程序的时候,为了提高效率,减少重复计算,我们往往会将一些计算结果不变的数据放置在循环的外面,上面的代码为了减少vec.end()的调用,把它移到for循环的外面了,因为这里认定了vec.end()并不会变。但是,这里却没有考虑vector的实现,以erase()函数为例,当删除了一个元素后,会将后面的元素覆盖删除的元素,因此,end的位置也必然会发生改变,由于只删除值为3的元素,当"vec[4] = 4"时,程序不会对越界的iter进行操作;当"vec[4] = 3"时,由于满足删除的条件,因此程序会对越界的iter进行删除操作,从而导致了crash的产生。下面将会画图介绍上面代码的操作过程。

当vec[4] = 4时:

       vec的初始状态是:


       当删除3之后的,vec的状态变为:


       从上图可见,当删除3之后,end的位置已经发生了改变,已经向前移动了一个位置,即指向原来vec[4]的位置,而vec[3]的值变为原来vec[4]的值(即4),因为上面的示例代码中的for循环使用了旧的end,因此for循环会一直执行,直到到达旧的end位置位置,由于新的end到旧的end之间再没有3存在,所以程序并不会执行具体的操作,因此程序正常运行。

当vec[4] = 3时:

       vec的初始状态是:

       当删除第一个3之后的,end的位置已经发生了改变,已经向前移动了一个位置,即指向原来vec[4]的位置,而vec[3]的值变为原来vec[4]的值(即3),vec的状态变为:

       当删除第二个3之后的,end的位置再次发生改变,指向了原来vec[3]的位置,而vec的状态变为:


       当删除第二个3之后,end的位置指向了原来vec[3]的位置,而因为上面的示例代码中的for循环使用了旧的end,因此for循环会继续执行,此时iter指向原来vec[3]的位置,程序发现vec[3] = 3,因此会执行erase操作,而此时iter所指位置已变为只读内存,尝试对其进行erase操作时,程序crash。
       引用Donald Knuth大师的一句名言:

              不成熟的优化是一切恶果的根源(Permature optimization is the root of all evil)

       因此,大家在尝试对旧的代码进行优化的时候,请多思考几遍,确定为什么要优化?优化会生效?怎么优化?不要单纯的以为代码量减少了,程序就能到优化,要知道存在即合理,旧的代码或许已经过时了,但是当时这样写也必然有它的道理,要深入分析之后才动手啊!切记!切记!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值