(/Please/ get a proper newsreader. It is nearly impossible to deal with
google-groups line split crap without making a mess.)
On 18/05/17 06:06, Bob Langelaan wrote:
> I teach a beginner C++ course and have taught my students that they
> should minimize the use of the break; and continue; statements. Recently
> I have been getting push back from some of my students with statements
> like "my friends at Microsoft tell me they use break; statements all the
> time" and similar types of objections.
>
/Minimizing/ the use of "break" is good - obsessing about it or banning
it is not. I find "continue" to be very rarely useful - I could happily
live without it in the language. But sometimes "break" really is the
best way to structure a loop.
> I created the post below for my students on this topic some time
> ago.
> I would be interested in getting feedback on my post and the topic in
> general.
>
> Thanks in advance,>
> Bob
>
> ===============================================================================
>
> Many beginner programmers will unfortunately use the break statement
> and the continue statement a great deal more than they should.
>
> I will go further and say that, generally speaking, a programmer
> should try to limit the use of break statements to exiting from a switch
> statement. And that the continue statement should almost never be used.
>
That paragraph does not read well. I first thought you had said that
"break" should not be used to exit switch statements either.
> Here are examples of using the break statement and continue
> statement
> that should normally be avoided:
>
> // An example of poor use of the break statement
>
> while (exit_criteria not met)
> {
> .
> .
> .
> if (exit_criteria_met)
> {
> break; // exit while loop immediately
> }
> .
> .
> .
> } // end of while loop
>
It is rare that you want to have the same test twice - but not /always/
bad. It is likely that in a case like this, you could remove the
condition at the start, making it a "while (true)" loop. In a "while
(true)" loop, it is perfectly clear that the loop exit will be done
somewhere in the middle of the loop (except for intentionally
never-ending loops).
>
>
> // An example of poor use of the continue statement
>
> while (exit_criteria not met)
> {
> .
> .
> .
> if (exit_criteria_met)
> {
> continue; // Go immediately to the end of the while loop.
> // Loop will exit with next iteration because
> // the exit_criteria has been met.
> }
> .
> .
> .
> } // end of while loop
"Continue" here is daft - it should be "break".
>
>
> In the 2 examples above, the exit_criteria is tested twice for each
> iteration of the while loop. This is considered both inefficient and
> poor programming style.
Don't claim that something "is considered" unless you can back it up
with clear justifications. Provide references from the Google C++ style
guide, or MISRA, or quotations from one of the C++ "greats". If you
cannot, then be honest and write "I consider it...".
And I don't think you can really claim it is inefficient, if the test
condition does not require external calls - the compiler will probably
be able to optimise that "continue" into a single jump to outside the
loop, just like a "break".
>
> The following example is also considered poor programming style
> because you should be exiting a loop at the beginning of the loop or at
> the end of the loop, and not in the middle of the loop:
>
Who says? /Why/ do you think that is a good idea?
This all comes across as very dogmatic to me. It looks like you want to
avoid exits in the middle of a loop simply because that is a rule that
/you/ were taught.
It is fine to say that exits in the middle of a loop are often hard to
follow, and make the flow of the code unclear. That is a /reason/ for
minimising the use of "break" and "continue". Saying they should be
avoided because it is "considered poor programming style" is not a
reason - any more than saying that "friends at Microsoft use break" is a
good reason to use them.
> // Another example of poor use of the break statement
>
> while (true) // The same as “while (1)” , but I prefer the former
(Side issue - I agree that "while (true)" is the nicest way to write
such loops. I dislike the use of "1" for a boolean, and I think the
alternative "for (;;)" is ugly. But beauty is in the eye of the beholder.)
> {
> .
> .
> .
> if (exit_criteria_met)
> {
> break; // exit while loop
> }
> .
> .
> .
> } // end of while loop
>
Often that is a perfectly good way to structure a loop. When you see
"while (true)" at the start of a loop, you are in no doubt that the exit
condition comes in the middle of the loop somewhere. If you don't know
that it is time to exit until the middle of the loop, then putting a
"break" (or perhaps "return") there seems perfectly reasonable.
Another situation where the use of "while (true)" and breaks can make
sense is if the exit criteria is complex. It may be natural to put
these at the beginning or the end of the loop, but it can be clearer to
have multiple statements, with space for comments, rather than jamming
everything into one exit expression. (There are, of course, alternative
restructurings - such as re-factoring the conditions into a function.)
So rather than:
while (A && B && !C) {
...
}
consider:
while (true) {
if (!A) break; // Exit if A does not hold
if (!B) break; // Exit if B does not hold
if (C) break; // Exit when C is reached
...
Sometimes code duplication is unavoidable. In such cases, choose to
duplicate the simplest, shortest, clearest code - not the major lines.
Testing "(miles == -1)" is far simpler and "smaller" than "cin >>
miles". Thus it is the test that should be duplicated, /not/ the action
of reading cin.
So while I agree that you may need to choose the lesser of two evils, I
think you have got the answer completely wrong in this case.
The best structuring here is very simple:
while (true) {
cout << "Enter the miles used (-1 to quit): ";
cin >> miles;
if (miles == -1) break;
...
}
Then there is /no/ duplication of either the important statements, or
the test.
>
>
> I will occasionally use a break statement in a situation similar to
> the next example:
>
>
>
> // In the example below we are searching for a value in an array
>
>
>
> bool found = false;
>
> for (unsigned int value ; I < ARRAY_SIZE; ++i)// iterate through the array
> {
>
> if (myArray[i] == search_value)
>
> {
>
> found = true;
>
> break; // exit loop since we have found search_value
>
> }
>
> }
>
> // When we reach this point, “found” will be true if search_value was
> // found in the array and false otherwise.
>
Baring typos in the code (which are a /really/ bad idea in a post to
students!), that is a perfectly good use of "break".
> I tutor students from schools all over the lower mainland on C/C++.
> Several of these students have told me in the past that their
> instructors do not allow _ANY_ use of break statements, other than in a
> switch statement. So how would you rewrite the previous example without
> using a break statement, but still be equally efficient? Quite simply
> actually:
>
The world is a big place, and this is an international newsgroup - no
one can guess where "the lower mainland" is.
I am always wary of hard and fast rules such as /never/ using break
statements. I need justification - the stronger the rule, the more
justification I need.
And efficiency should not be your major concern here - code clarity and
correctness trump efficiency every time. If you are going to have a
rule about code style, ask yourself:
Does it make it easier to write correct code?
Does it make it harder to write incorrect code?
Does it make it easier to see that correct code is correct?
Does it make it easier to see that incorrect code is incorrect?
For the most part, pick efficient algorithms and let the compiler worry
about the efficiency of the detail in the code. The compiler is not
going to follow your structure of loops and breaks anyway.
>
>
> bool found = false;
>
> // iterate through the array
> for (unsigned int value ; (!found) && (i < ARRAY_SIZE); ++i)
> {
>
> if (myArray[i] == search_value)
>
> {
>
> found = true;
>
> }
>
> }
>
In my book, the complex condition in the for loop makes it poor code -
it is unclear. It is not as bad as using "i = ARRAY_SIZE" to jump to
the end of the loop, but it is still bad. This kind of exercise is a
very artificial problem, and there are no nice answers. They can be
useful while learning, but that does not make them good style in practice.