A “Style
Guide” is a set of rules intended to promote good programming style. The
idea is not unique to this course, or indeed to university programming
courses. Style guides also exist in the real world, where they have
the additional advantage of imposing a degree of uniformity throughout a
company’s programs (so making it much easier for one programmer to modify a
program written by somebody else).
Style
guides are not a panacea. It is perfectly possible to produce programs
which conform to the letter of a guide but which are nonetheless hopelessly
convoluted (as many students will doubtless prove). It can safely be
said, however, that conforming to the style guide will greatly increase your
chances of producing good programs
THE SYSC1102 STYLE GUIDE
1.
Indent Your Code
There is a
lot of room for disagreement upon the finer details of “bad” and “good”
style. Some people, for example, think that the “break” statement
represents poor practice while others argue that it makes for better
programs. Nobody, however, disputes that proper indenting is the very
foundation of good programming style. Proper indenting never hurts, and
it makes programs immeasurably easier to understand.
There are a
number of indenting styles. Each has it advantages, it drawbacks, its
advocates, and its detractors. While we insist that you
consistently make use of one of these styles, you may use whichever one of them
suits you best.
Style I (all squiggly brackets on lines by themselves)
if
(<condition>)
{
<statements>
}
else
}
<statements>
}
while
(<condition>)
{
<statements>
}
do
{
<statements>
}
while
(<condition>);
Style II (all closing squiggly brackets on lines by
themselves)
if
(<condition>) {
<statements>
}
else }
<statements>
}
while
(<condition>) {
<statements>
}
do {
<statements>
}
while
(<condition>);
Style
III (Compact)
if
(<condition>) {
<statements>
} else {
<statements>
}
while
(<condition>) {
<statements>
}
do {
<statements>
} while
(<condition>);
The “short”
forms of the “if” statement and so on are not permitted - you must use a
(properly positioned) pair of squiggly brackets even when only a single
statement is required.
Some
programmers recommend adding a comment to the closing bracket of each construct
(as shown below). Others, however, argue that this gives a false sense of
security because no error will be reported if one gets things wrong (and, for
example, puts “end if” at the end of a while).
if
(<condition>) {
<statements>
} else {
<statements>
} // end if
It can
probably be said that this technique is most useful in the case of large
programs in which an “if” or “while” may extend over several pages of
code. Its use in this course is optional.
· When a series of assignments are being done in a sequence, align the assignment operators vertically, as follows
dayRecord.date
= yesterdaysDate +1;
dayRecord.dayInWeek = next(dayInWeek);
dayRecord.month = next(currentMonth);
dayRecord.year = currentYear;
· When a sequence of declarations is being done, align the consistent parts, such as const, signed/unsigned, type, name, assignment operator and value; For example:
const unsigned int SOME_LARGE_NUMBER = 3000000000;
signed int aVariable;
const signed
short A_SHORT_NUMBER
= 20000;
2.
Don’t Use Goto’s
In the
hands of an expert programmer, goto’s can, from time
to time, actually improve a program. In particular they can be usefully
employed to get around the unfortunate fact that C++ does not otherwise support
breaking out of nested loops. In general, however, goto’s
tend to lead to muddling thinking and to bad programs (generally known as
“spaghetti code”). The goto is thus prohibited.
3. “break”statements
All switch-case constructs shall have a "break" statement at the end of each non-empty case.
Rationale:
Missing break statements are a cause for some rather famous and serious
software failures (for example - Fall 1989 it
caused the shutdown of the Eastern
4.
Use Meaningful Variable Names
There are
cases in which terse, single character variable names are in fact
appropriate. Consider, for example, the following code segment, which
prints out a message ten times.
for (i = 0; i < 10; i++) {
cout << “Hello world!”
<< endl;
}
In this
case absolutely nothing would be gained by replacing “i”
with a more “meaningful” name. Indeed using a longer name would probably make
the code segment harder rather than easier to understand. Other
examples could be offered. If the function of a piece of code is to
determine the value of “x”, “x” may be a quite reasonable variable name, and
“n” is commonly used to represent the size of an array.
In general,
however, variable names should be descriptive. Thus “speed” is much better
than “s”, and “population” is superior to “p”. It is often a good idea to
use multiple words (separated by underscores, or with
the first letter of each word capitalized) in naming variables.
Thus, for example, one might have “initial_population”,
or “sizeOfRoom”.
The test of
a good variable name is whether or not it conveys the role of the variable to
the reader. While sometimes single character names will pass this test,
this can be expected to be the exception rather than the rule. If all (or
most) of your variables have single character names, you can be pretty sure
that your program will not be acceptable.
5.
Avoid Magic Numbers
A “magic
number” is a constant which leaves the reader wondering just what it
represents, and where on earth it came from. Consider, for example, the
following statement:
angular_position =
57.29578 * (omega / i);
While some
might recognize the value, its significance (and hence the intent of the
statement) is far from clear. The value 57.29578 is a “magic value”.
Magic values are not good style and should be avoided by using named constants
as shown below.
const double degrees_per_radian = 57.29578;
....
angular_position = degrees_per_radian * (omega / i);
Not only is
the assignment statement now much more self-explanatory, but, if the constant
were used in a number of places, there would much less chance of a typing error
leading to the use of an incorrect value.
While all
magic numbers are constants, not all constants are magic numbers. In the
case below, the zero is just that. The reader knows what it
represents, and does not wonder where it came from. Replacing the
“0" with a named constant called “zero” would contribute nothing to the
clarity of the program, and would instead just make it wordy.
if (speed
< 0) { // speed is negative
cout << “We’re going
backwards.” << endl;
}
6.
Put All Declarations First
In C++ it
is permissible to intermingle “declarative” statements
(the declaration of variables and constants) with what might be called
“executable” statements (cin’s, cout’s,
assignments, and so on).
We strongly
recommend, however, that students not mix things up, and instead declare first
and execute afterwards If all declarations
are kept together you’ll always know where to look for them, and, more
importantly, you’ll avoid playing with fire. Declaring variables “on the
fly” raises a number of issues which can get a bit tricky and which we won’t be
covering in this course. Consider, for example, the following code
segment:
int b;
...
if (b <
0) {
int a = 7;
} else {
int a = 9;
}
cout << a << endl;
If you can
understand why it will result in an error message to the effect that “a” is
undefined, you are well ahead of the game and are not the kind of student we’re
worried about. If this makes no sense, on the other hand, you’re well
advised to avoid getting in over your head. Just put all your
declarations first and this kind of thing won’t crop up.
One
somewhat special case is that of declaring a variable within a “for” loop as
shown below.
for (int i = 0; i
< 10; i++) {
<statements>
}
While some
would argue that this represents much better style than declaring “i” at the beginning of the function, it also has the
potential to leave beginners very confused. If one breaks out of
the loop, for example, “i” cannot be examined to
determine when the break occurred. This is because, if declared in this
fashion, “i” only exists for a long as the loop is
being executing. Again, if this all sounds a bit scary, just don’t take
advantage of this language feature. Declare “i”
at the beginning, write the loop as “for (i = 0;
...”, and everything should work as you expect it to.
7. Arithmetic operations
Do arithmetic only on objects of the same type. If arithmetic on variables of different types must be done, explicitly cast one to the type of the more general type before performing the operation.
Rationale:
The automatic promotion of types can get you in trouble! Truncation, rounding
and division are slightly different depending upon the types used and how they
are combined. This can lead to subtle (but possibly serious) errors.
8. Enumeration
type objects
Enumeration type objects may
only be compared with objects of the same type, incremented and decremented
i.e, addition,
subtraction, comparison with numeric or other enumeration types are forbidden
Rationale:
Enumeration types are not integers, even though their internal representation
is the same as integers..It
is a fundamental error to compare or do arithmetic on literals that are from
different enumerations (eg - comparing SUNDAY and
RED), or to compare them with numbers.
9.
increment/decrement operators
Do not use "++" or "--" in complex statements - i.e., only use as the single instruction in a statement, for example
x++;
//OK
y = name_array[x++]; //Avoid!!!
Rationale:
Code that uses these forms is tricky and difficult to decipher, especially for
non-C++ experts. Often the variable which is desired to be incremented (or
decremented) is used more than once in the same expression. In such cases it is
undefined by the language in which order the increment and the other uses of
the variable occur. Code that uses such mechanisms is unportable
and often unpredictable.
10. Comments
Comments - must not repeat the code, only explain the algorithm