Different styles of flow of program?
I am a computer science student therefore I do not know that much.
I was recentl开发者_运维百科y talking with a friend who just got a job as a (java) software developer. He told me that in his job there is a guy who is really experienced in C++, but unfortunately every time he writes code in java, he is using the try-catch to control the flow of the program. According to my friend this is a wrong style in Java. Is this true? What are the differences (if any) in using try-catch(-finally in java) between C++ and Java?
Using try-catch to control the flow of the program is wrong anywhere... Exception handling is what it says it is: Handling of exceptional circumstances.
Of course for every rule there are a dozen counter-examples of necessary deviations, but generally speaking: Don't control program flow with exceptions.
Using exceptions for controlling the flow of a program occurs when you anticipate certain exceptions being thrown in a normal operating environment, and you make logical decisions based on those exceptions.
For example controlling program flow in pseudo code:
try {
write update to file
} catch (IOException) {
write update to alternate file
}
In this case it would be better to actually test for path existence before blindly performing the write.
I removed the permission checking notes because it's a bad example
A good usage of exception handling: (pseudo code again)
try {
do stuff
} catch(OutOfMemoryException) {
fail gracefully (don't try and do something else to achieve the same result)
}
I would say that from a program architecture point of view the purpose of exceptions is the same in Java and C++. They should be used to handle exceptional cases, not the direct the normal flow of the program.
The most common argument against use of exceptions is that they are slow. Like most arguments based on code speed it's almost always irrelevant nowadays, especially when exceptions are used properly.
I think what he is saying is that he's using exceptions for non-exceptional conditions like normal loop terminations. That is considered a no-no by programmers in most languages that provide exceptions, including C++.
The reason for this is that exceptions tend to drag a fair bit of information along with them. On some platforms, they bring the entire callstack at the point of the exception, for the purposes of displaying it in a nice debug message. That's very expensive, if all you really want is a unconditional control jump.
Instead, you should be using statements like break
, return
, or even (in a real pinch) goto
.
I would expect the C++ and Java worlds to be largely similar in their views:
- flow control with try/catch blocks isn't immediately obvious
- it's expensive performance-wise. In practise this may not be an issue, but in many scenarios (e.g. tight loops) it will certainly impact you.
The sole purpose of try
/catch
is flow control. It is intended to allow us to break out of any loop, and even go past the caller and up to a higher-level function.
However, it's designed for exceptional conditions, such as null references being used, division overflowing or file I/O failing. These are errors that prevent the program from continuing on and must be handled. Generally, they are hard to predict or are very rare.
When an exceptional condition arises, the very best thing to do is to alter the flow of control so that the exception is thrown as far up as it has to be in order to find a catch block willing to handle it. Any method in between gets aborted and unwound, even if it has no code to check for results.
When people complain about using try
/catch
for flow control, what they should mean is that it's not for flow control in non-exceptional conditions. If all you want to do is exit a loop, use break
or return
; don't throw an exception and then catch it. That's not what exceptions are for, nor is it what they're good for.
The try-catch block is intended to do the same thing in all languages- exceptions are one of the many ways of recovering when something goes wrong. In java, the following two statements are identical:
if(x != null){
//do the normal thing
}
else{
//recover from the error
}
and
try{
//do the normal thing
}
catch(NullPointerException ex){
//recover
}
However, using exceptions is never good idea for a number of reasons:
- the cost associated with throwing the exception (ie generating a Throwable in Java)
- the flow of control is not immediately obvious- for instance, if a NullPointerException can be thrown by multiple statements in the try block, how can you tell which one threw it in the catch block
- etc, etc
What he may be wanting is multiple ways to not execute a bit of code... so rather than (in java)
if (cond1) {
some_code();
if (cond2) {
some_more_code();
if (condN) {
ad_infinitum
}
}
}
This can be 'flattened' by throwing some garbage...
try {
if (!cond1) throw new Throwable();
some_code();
if (!cond2) throw new Throwable();
some_more_code();
if (!condN) throw new Throwable();
ad_infinitum();
} catch (Throwable ignore) { }
The problem is what if some real exception was thrown? I think what your colleague should write if he wants to flatten the indentation is something like this:
do {
if (!cond1) break;
some_code();
if (!cond2) break;
some_more_code();
if (!condN) break;
ad_infinitum();
} while (false);
Since do/while always executes we can use the break to approximate a goto (as long as it all goes to the same spot).
Your question is too vague without sample code. There is no finally
in C++.
EDIT In general, you don't want to use exceptions
as a means to control the flow. Exceptions
are supposed to be used only for exceptional (unexpected situations) cases.
精彩评论