GotW #2 临时对象

原文在http://www.gotw.ca/gotw/002.htm
自己翻着学的。看过Exceptional C++的可以跳过。想批评啥说啥,随您
临时对象:
难度:5/10
(比#1高了一点了)

多余的临时对象是个坏蛋惯犯。它总能把你的活计从窗户扔出去,或者让你的代码效率低下。

问题:
你在做code review,(可能你是菜鸟,还没机会review别人的。那就当成是在review自己的代码好了)码农写了个函数,里边至少用了三次非必要的临时对象。(凶手这下是3个以上了)
你能找出几个?该怎么改正?
string FindAddr( list<Employee> l, string name )
  {
    for( list<Employee>::iterator i = l.begin();
         i != l.end();
         i++ )
    {
      if( *i == name )
      {
        return (*i).addr;
      }
    }
    return "";
  }
(我找了下,找到了3个,有个地方是bug,非改不可。不知道对不对。。 看看答案)

答案:
你信不信,就这么几行就有茫茫多的问题。。。(不愧为码农。。)3次明显的多余临时对象,2个较隐蔽的,1个胡来的。

string FindAddr( list<Employee> l, string name )
                         ^^^^1^^^^       ^^^2^^^
1和2都应该是const引用。传值会导致对两个对象的复制,内存不是拿来这么浪费的呀,当然还有cpu
[规定]多用const引用,少用值传递。
for( list<Employee>::iterator i = l.begin();
         i != l.end();
         i++ )
         ^3^
3.这个比较隐蔽。前缀++比后缀++的效率高些。为什么呢?想想他们两个的返回值吧。后缀++会自己增加1,然后把一个带有旧值的临时变量给返回去。这一点对int之类的内部类型同样适用。
[指导意见]:用前缀,这里别用后缀。
if( *i == name )
        ^4
4.尽管Employee类没列出来,这里有两种可能,把Employee转换成string,或是调用Employee::Employee(string &)做转换。不管是哪种,都会创建一个临时对象,然后调用operator==(),可能是对string的,也可能是Employee。(除非重载operator==(),或者Employee能被转成string引用,而这也是非必要的临时对象)
[指导意见]:注意类型转换时很隐蔽的临时对象。可以的话,把构造函数声明为explicit就能避免。
return "";
           ^5
5.这里会生成一个临时对象。空string对象。
如果用局部string变量做返回值,并且专门返回它会更好。这样编译器能做一点优化。比如: string a = FindAddr( l, "Harold" );
[规定]单入口单出口。不要写多个返回语句。
注意:经过Herb Shutter(也就是原文作者)测试,这个方法已经被否了。。。(我觉得这个点子本来就非常雷人,他这种大仙怎么会有这种想法的)
在Exceptional C++里,他写了一大堆测试的结果后,说
[规定]小心对象的生存期。永远永远不要返回局部自动对象的指针或引用。因为完全用不上。当然更可怕的是万一被用到了,就更傻眼了。。。
他甚至写了用static局部变量,然后返回引用。可想而知这个点子也不怎么样。不过好在,他又发现一个问题,就是
 i != l.end();
 ^^^6^^^
6.这是在for循环中的判断条件,很显然,在每一次循环的判断中,l.end()都会生成一个临时对象,然后返回。而这个对象每次都一样,这是一个严重的浪费。改正的办法很简单。在for循环前,把l.end()赋给一个局部对象就可以了,只需要生成一次。
[指导意见]:把恒定的值预先算出来。

最后给一个《Exceptional C++》中改好的
string FindAddr( list<Employee> & l, string & name )
{
   list<Employee>::const_iterator end(l.end());      //const_iterator和#1里说了的初始化SomeType t(u)
   for( list<Employee>::const_iterator i = l.begin();
      i != end;
      ++i )                              //前缀++
   {
      if( i->name == name )   //这里和下面的i->addr都算是他的想象了
      {
         return i->addr;            //同上
      }
   }
   return "";
}

这一篇参考了《Exceptional C++》原文。

posted on 2012-02-21 21:40 高兴 阅读(198) 评论(0)  编辑 收藏 引用 所属分类: GotW


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2012年2月>
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910

统计

常用链接

留言簿

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜