Guru of the Week 条款20:代码的复杂性(第一部分) (转)

Guru of the Week 条款20:代码的复杂性(第一部分) (转)[@more@]

GotW #20 Code Complexity – Part IXML:namespace prefix = o ns = "urn:schemas-microsoft-com:Office:office" />

著者:Herb Sutter

翻译:K ][ N G of @rk™

[声明]:本文内容取自www.gotw.ca网站上的Guru of the Week栏目,其著作权归原著者本人所有。译者kingofark在未经原著者本人同意的情况下翻译本文。本翻译内容仅供自学和参考用,请所有阅读过本文的人不要擅自转载、传播本翻译内容;下载本翻译内容的人请在阅读浏览后,立即删除其备份。译者kingofark对违反上述两条原则的人不负任何责任。特此声明。

Revision 1.0

 

Guru of the Week 条款20:代码的复杂性(第一部分)

 

难度:9 / 10

 

(本条款提出了一个有趣味的挑战:在一个简单得只有三行代码的函数里可以有多少条执行路经?其答案几乎将肯定让你吃惊。)

 

[问题]

在没有任何其它附加信息的情况下,下列代码中可以有多少条执行路经?

  String EvaluateSalaryAndReturnName( Employee e )

 
   

  {

 
   

  if( e.Title() == "CEO" || e.Salary() > 100000 )

 
   

  {

 
   

  cout << e.First() << " " << e.Last()

 
   

  << " is overpaid" << endl;

 
   

  }

 
   

  return e.First() + " " + e.Last();

 
   

  }

 
   

 

[解答]

 

假设:

a)  忽略对函数参数求值时的不同顺序以及由析构函数(destructor)抛出的异常。[注1]

 

  下面的问题提给无所畏惧的勇者:

  如果允许析构函数抛出异常,那么共会有多少条执行路经呢?

 

b)  调用的函数被认为具有原子性。事实上,例如”e.Title()”这个调用就可能由于好几个原因而抛出异常(比如,它自己本身可能抛出异常;它也可能由于「未能捕获由其调用的另一个函数所抛出的异常」而抛出异常;或者它可能采用return by value(传值返回)方式从而造成临时对象得构造函数可能抛出异常)。这里我们假设对于函数而言,只关注执行e.Title()操作的结果,即完成该操作后是否抛出了异常。

 

解答:23(仅仅在4行代码里!)

 

如果你找到了    给自己评等级

---------------------------------------------------------------------

3      平均水平(Average)

4-14      能够认知异常(Exception-Aware)

15-23      精英资质(Guru Material)

 

这23条执行路径包括:

  ——3条与异常无关的(non-exceptional)路径

  ——20条暗藏的路径,都与异常有关

 

要理解那3条普通路径,诀窍就是要知道C/C++的“短路求值规则(Short-Circuit Evaluation Rule)”:

 

1.  如果e.Title()==”CEO”,那么就不需要对第二个条件求值了(比如,e.Salary()将不会被调用),但cout还是会被执行的。[注2]

2.  如果e.Title()!=”CEO”但e.Salary()>100000,那么两个条件都会被求值,cout也会被执行。

3.  如果e.Title()!=”CEO”且e.Salary()<=100000,那么cout将不会被执行。

 

下述都是由异常引出的执行路径:

 

  String EvaluateSalaryAndReturnName( Employee e )

 
   

  ^*^  ^4^

 
   

4.  引数采用pass by value(值传递)方式,这将唤起Employee copy constructor。这个copy操作可能抛出异常。

 

*.  在将函数临时的返回值拷贝到函数调用者的区域时,String的copy constructor可能抛出异常。然而在这里我们忽略这种可能性,因为其是在函数外部发生的(何况从目前的情形来看,现有的执行路径已经够我们忙的了!)

 

  if( e.Title() == "CEO" || e.Salary() > 100000 )

 
   

 ^5^  ^7^  ^6^ ^11^  ^8^  ^10^  ^9^

 
   

5.  成员函数Title()本身就可能抛出异常;或者其采用return by value方式返回class type的对象,从而导致拷贝操作可能抛出异常。

6.  为了与有效的operator==相匹配,字符串也许需要被转换成class type(或许与e.Title()的返回型别相同)的临时对象,而这个临时对象的构造过程可能抛出异常。

7.  如果operator==是由程序员提供的函数,那么它可能抛出异常。

8.  与#5类似,Salary()本身可能抛出异常,或者由于其返回临时对象而造成在临时对象的构造过程中抛出异常。

9.  与#6类似,可能需要构造临时对象,而这个构造过程可能抛出异常。

10.  与#7类似,这或许是由程序员提供的函数,那么它可能抛出异常。

11.  与#7和#10类似,这或许是由程序员提供的函数,那么它可能抛出异常。

 

  cout << e.First() << " " << e.Last()

 
   

   ^17^   ^18^

 
   

  << " is overpaid" << endl;

 
   

12-16  如C++标准草案所述,这里的五个对operator<

17-18  与#5类似。First()和/或Last()可能抛出异常,或者由于其返回临时对象而造成在对象的构造过程中可能抛出异常。

 

  return e.First()  +  " "  +  e.Last();

 
   

   ^19^  ^22^^21^ ^23^  ^20^

 
   

19-20  与#5类似。First()和/或Last()可能抛出异常,或者由于其返回临时对象而造成在对象的构造过程中可能抛出异常。

21.与#6类似,可能需要构造临时对象,而这个构造过程可能抛出异常。

22-23  与#7类似,这或许是由程序员提供的函数,那么它可能抛出异常。

 

本期GotW条款的目的是演示「在一个允许异常机制的语言中,简单的代码里可以存在多少条暗藏的执行路径」。这种暗藏的复杂性会影响函数的可靠性和可测性吗?请在下一期GotW中寻找这个问题的答案。

 

[注1]:决不允许一个异常从析构函数中渗透出来。如果允许这样做,代码将无法正常工作。请看我在C++Report Nov/Dec 1997中有关的更多讨论:Destructors That Throw and Why They're Evil。

 

[注2]:如果对==、||和>予以正确恰当的重载(overload),那么在if语句中,||或许是一个函数调用。如果其是一个函数调用,那么“短路求值规则”会被抑住,这样if语句中的所有条件将总是被求值。

(完)


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10752043/viewspace-991434/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/10752043/viewspace-991434/

你可能感兴趣的:(Guru of the Week 条款20:代码的复杂性(第一部分) (转))