从一个示例反思如何避免c++中的临时对象

说明

临时对象是隐晦的,但对性能的影响是不可忽视的。

本文通过剖析示例,找出临时对象的藏身之所,曝光并消灭它。

示例

代码片断如下:

string FindAddr(list<Employee> l, string name)
{
	for (auto i = l.begin(); i != l.end(); i++)
	{
		if (*i == name)
		{
			return (*i).addr;
		}
	}
	return "";
}

这段代码简单到不需要任何的注释,且可能存在于任何规模的项目中。但,这里面有多少处临时对象?

剖析

先说下答案吧,至少有 5处:

  • 函数参数list<Employee> l, string name:使用传值的方式,性能代价高昂。应该使用引用。
  • 循环体中的i++,后置操作符时,对象不但自己递增,还要返回一个包含递增前值的临时对象。内建int i++也是如此。应该使用前置操作符。
  • 条件判断语句:*i == name,隐含一个隐式转换,从Employee到string,要么通过构造函数,要么操作符,这两种方式都会产生临时对象,可以使用显式声明(explicit)避免。
  • 返回语句:return "";,会产生一个临时的string对象。可以先定义一个局部string来储存返回值,再使用return返回,利用返回值优化。
  • 函数返回类型:string。这是个陷阱,如果把返回类型修改为引用(string&),那么再返回局部变量就会导致对局部对象的引用,造成未定义的行为。

好了,看到了这些问题,也知道了正确的做法,修改版本如下:

string FindAddr(list<Employee>& l, string& name)
{
	string addr;
	for (auto i = l.begin(); i != l.end(); ++i)
	{
		if ((*i).name == name)
		{
			addr = (*i).addr;
			break;
		}
	}
	
	return addr; // 利用了RVO(返回值优化,函数的返回类型与局部变量的类型完全一致时会启用)
}

程序没问题了,但对于标准容器使用遍历式的循环,还是有点不顺眼。

使用标准库

读过《Effective STL》的小伙伴们,是不是也有种对手写循环的反感?那就对了,因为可以使用算法!

使用标准库的算法的好处就不多说了,直接上代码:

string FindAddr(list<Employee>& l, string& name)
{
	string addr;
	auto i = find(l.begin(), l.end(), name);
	if (i != l.end())
	{
		addr = (*i).addr;
	}
	
	return addr; // 利用了RVO(返回值优化,函数的返回类型与局部变量的类型完全一致时会启用)
}

一句话:利用标准库的算法,不要重复造轮子。这样更好、更快、更强!

小结

简单的代码中隐藏着这么多的临时对象,它们看不到,但我们必须要知道。

这对于性能提升来说是必要的,因为只要简单地改动就能达到。

注重效率,从写代码开始。

参考资料

《Exceptional C++》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值