2010-05-20 53 views
7

在我执行操作之前,有些地方会检查有效的指针;这些检查有时可以嵌套得相当深。嵌套如果(x)检查 - 写这个更好的方法?

例如,我有

if (a) 
{ 
    if (a->b()) 
    { 
    if (a->b()->c()) 
    { 
     a->b()->c()->DoSomething(); 
    } 
    } 
} 

我真的不喜欢这个样子。有没有办法把它变成更可读的东西?理想情况下,

if (a && a->b() && a->b()->c()) 
{ 
... 
} 

会很好,但显然不会工作。

编辑-nvm我举的例子是否像大家都指出的那样工作。我做了测试,看看它是否有效,但是在我的测试中我的代码中存在一个错误。咄!

+6

为什么不工作? – 2010-05-20 16:42:51

+3

它显然*会*在你发布的例子中工作。在你遗漏的“其他”案件中发生了什么? – 2010-05-20 16:43:19

+2

是什么让你觉得不起作用?短路评估几乎可以保证两者意味着相同的事情(除非你已经重载'operator &&')。 – 2010-05-20 16:43:27

回答

28

为什么后者不起作用?

在C中,& &是一个短路操作符,所以它从左到右评估,如果任何评估是错误的,评估停止。

事实上,你可以写:

a && a->b() && a->b()->c() && a->b()->c()->DoSomething(); 
+6

是否可以写一个'&& A-> B()&& A-> B() - > C()&& A-> B() - > C() - > DoSomething的();'取决于返回类型'DoSomething'。如果'DoSomething'返回无效,那是不合法的。 – 2010-05-20 17:23:08

+0

@R Samuel Klatchko:这只有在你把这个表达式放在一个if语句中时才适用,不是吗? ......在WhirlWind的例子中没有必要。 – 2010-05-20 17:43:01

+0

@丹尼尔 - 不,因为它是&&运算符的RHS,它必须具有*某些*值。 void函数不是有效的RHS。 – nsayer 2010-05-20 17:45:24

5

你的第二个例子确实在C/C++的工作。当第一个FALSE值被击中时它会短路。

+0

它也适用于Java。 – nsayer 2010-05-20 17:47:34

+0

@sayer:是的。好点子。 – 2010-05-20 17:54:34

3

为什么“显然不会工作”?由于&&运营商只评估左边有效的正确条款,重写是完全安全的。

13

K&R 引用:由&&||连接

表达式由左到右进行评价,并且可以保证评价将尽快停止作为真或假是已知的。

因此,后者的例子将完美工作,因为WhirlWind has noted


The C Programming Language,第二版,第21页

+0

感谢您的报价。我试着用google搜索这种东西,但不知道google的话是什么。不用说,我没有发现这一点,当我自己测试它时,看看它是否从左到右评估,我在那里有一个导致它崩溃的bug。 – Will 2010-05-20 17:06:43

2

if (a && a->b() && a->b()->c()) { a->b()->c()->DoSomething(); }

C++进行延迟计算,从而这将工作。首先,a将被评估,如果它是0,则整个条件是错误的,因此不会评估其他部分。

这也适用于||运营商。如果您编写if (a || b),如果a为真,则不会评估b。

4

您已经从其他答案中看到,使用& &可以工作,并且会在遇到空指针时短路评估。

我这个不安的程序员喜欢避免重复这样的测试方法调用,因为它避免了担心它们是否是幂等性的。一种选择是改写这样

A* a; 
B* b; 
C* c; 

if ((a=a()) && (b=a->b()) && (c=b->c())) { 
    c->doSomething(); 
} 

诚然冗长,有点笨重,但至少你知道每一个方法被调用一次。

+0

如果'a'是一个指针,那么使用它的调用不需要是'a-> b()'? – 2010-05-21 04:20:45

+0

d'oh - 是的。这就是我的意思,但没有写。我一直在做java太久...... – mdma 2010-05-21 06:01:32

0

第二个将工作提供了你只想DoSomething()打电话。

3

既然你已经收到了直接回答你的问题我就提电话的是长链,就像你到了那里是一个代码味道,你可能会考虑一个更好的设计。这样的设计可能,在这种情况下,包括使用空对象模式,使您的通话可能只是归结为:

a->CDoSomething(); 
+1

[Demeter法](http://en.wikipedia.org/wiki/Law_of_Demeter) – 2010-05-20 21:27:19

3

链接的作品,但它不一定是最好的一般情况下的答案,特别是因为它掩盖了失败点。我会建议通过反转逻辑来使测试变平,以便在失败时退出。

if (!pa) 
    return Fail("No pa"); 

B* pb = pa->b(); 
if (!pb) 
    return Fail("No pb"); 

C* pc = b->c(); 
if (!pc) 
    return Fail("No pc"); 

pc->DoSomething(); 

同样的事情,但平坦,易于阅读。此外,由于它立即处理失败案例,因此它不会被降级到您可能从未写过的else

在这个例子中,我以为你不想只是默默地消失,所以我加了Fail作为记录文本并返回false一个帮手。你也可以抛出异常。事实上,如果各种方法通过抛出一个适当的异常而不是返回null来表示它们的失败,那么所有这些都是不必要的。如果无声失败是可取的,那么空对象模式将是适当的。

相关问题