Reputation:
First, I realize that from a performance perspective, the design of this switch statement is slow because cout is being called several times in certain cases. That aside, is this style of writing a switch statement not good coding practice. In other words, would it be better to handle each case individually and break or is the fall-through better?
int main(void)
{
int number;
cout << "Enter a number between 1 and 10 and I will display its Roman numeral equivalent." << endl
<< "> ";
cin >> number;
cout << "Roman numeral: ";
switch (number)
{
case 3:
cout << "I";
case 2:
cout << "I";
case 1:
cout << "I";
break;
case 4:
cout << "I";
case 5:
cout << "V";
break;
case 6:
cout << "VI";
break;
case 7:
cout << "VII";
break;
case 8:
cout << "VIII";
break;
case 9:
cout << "I";
case 10:
cout << "X";
break;
default:
cout << "Error!\nYou did not enter a number between 1 and 10";
}
cout << endl;
return 0;
}
Upvotes: 2
Views: 2943
Reputation: 75130
Switch statements aren't slow, they're usually optimised to jump-tables by the compiler. And if you're sure that switch statement works as expected, it's fine and is a pretty cool way of doing it.
That said, you'd have the same number of cases if you handled each one individually. If that's all you're doing, I'd probably change it to handle each one seperately and not do fall through. It's more comprehendable and easily maintained that way:
switch (number)
{
case 1:
cout << "I";
break;
case 2:
cout << "II";
break;
case 3:
cout << "III";
break;
case 4:
cout << "IV";
break;
case 5:
cout << "V";
break;
case 6:
cout << "VI";
break;
case 7:
cout << "VII";
break;
case 8:
cout << "VIII";
break;
case 9:
cout << "IX";
break;
case 10:
cout << "X";
break;
default:
cout << "Error!\nYou did not enter a number between 1 and 10";
}
And like @paxdiablo suggested, to enhance readability you can put the case, statement, and break
all on the same line if it looks better to you:
case 1: cout << "I"; break;
case 2: cout << "II"; break;
// etc.
Upvotes: 4
Reputation: 109079
Switch statements can be optimized into jump tables by the compiler, so they're not always slow. And they're definitely better than writing a bunch of if
- else if
statements. I personally like fall through because it allows you to do some cool stuff in certain cases without having to repeat code; but in general, they are frowned upon because they can be harder to understand than handling each case individually.
As for your example, if you're worried about multiple calls to cout
, you can always store the intermediate strings in a stringstream
and print the final string. However, output to cout
is buffered, so I don't know whether this will have any significant performance improvement.
#include <iostream>
#include <ios>
#include <sstream>
int main(void)
{
using namespace std;
int number = -1;
cout << "Enter a number between 1 and 10 and I will display its Roman numeral equivalent." << endl
<< "> ";
cin >> number;
ostringstream oss( "Roman numeral: ", ios_base::ate );
switch (number)
{
case 3:
oss << "I";
case 2:
oss << "I";
case 1:
oss << "I";
break;
case 4:
oss << "I";
case 5:
oss << "V";
break;
case 6:
oss << "VI";
break;
case 7:
oss << "VII";
break;
case 8:
oss << "VIII";
break;
case 9:
oss << "I";
case 10:
oss << "X";
break;
default:
cout << "Error!\nYou did not enter a number between 1 and 10";
return -1;
}
cout << oss.str() << endl;
return 0;
}
Upvotes: 0
Reputation: 881093
I tend not to use that style since it's often difficult to figure out the flow of control at a single glance. This is one reason why goto
is generally considered a bad idea (though some people take that as gospel without understanding why - it's actually quite handy in some circumstances provided it doesn't render the code unreadable).
In other words, I prefer each case
to be independent. If they have commonality, I will tend to break that out into separate functions and call those functions from each case.
That doesn't cover situations where the code is identical for different cases, where I just use something like (pseudo-code, obviously):
case 1: case 2: case 3:
print "It's one, two or three"
For your specific use case, I would probably just use a table lookup like:
char *roman[] = {"I", "II", "III", "IV", ... "X"};
if ((n < 1) || (n > 10))
cout << "Urk! I only have ten fingers!";
else
cout << roman[n-1];
just to keep the (source) code compact.
Upvotes: 1
Reputation: 18133
It's such a simple case, it's hard to say it's really "bad." I've heard differing opinions on lettings cases fall through into other cases. And I'm sure at some point, we've all done that. (I usually document it quite clearly with something like /* FALL-THROUGH */ so the next person reading the code knows I meant to do that.)
I think a more complex example would demonstrate it's not such a great idea. But not really because a fall-through itself is a bad thing. But because a better design wouldn't warrant it. In object oriented design, a case statement might indicate that you've got a "code smell" -- that you're not letting the object do what it needs to based on type rather than based on some other piece of information.
Now, if someone really wanted to get picky about your admittedly simple example, one could say you're mixing controller, model, and view together in bad ways. Your controller should simply get the input. Your model would have better ways of obtaining the alternate representation of the input (a map, or heck, a case statement, I dunno), and your view logic would not be scattered in and around your controller logic. By actually adhering to some other design concepts, the switch statement might vanish entirely. That might happen with other examples, too.
In short, I think if your design is sound, you might find that if a switch statement IS even in fact necessary, then the concern about a fall-through case statement isn't a big deal.
Upvotes: 1
Reputation: 34655
That is very clever, and this will be plenty fast. But if you want to improve performance ...
cout
ing to a running string buffer. Then, after the switch statement, cout
the buffer.Upvotes: 0
Reputation: 375
You are right in that calling the '<<'-operator several times incurs a certain amount of overhead. However, you're talking about two statements here so this is probably not the point to optimize.
If you want to optimize the code, why not use a static array of strings containing the roman numbers? Like roman[0] = "I", roman[1] = "II", etc. I doubt this representation will cost you more memory than the above function and you get rid of the bloated switch statement.
Upvotes: 0
Reputation: 357
Using the Break for each case should be the perferred option. Using "Fall-Through" will move on to the code for the next case, which could cause errors and hinder performance.
Upvotes: 0