Style or panache is one of the ways programmers separate
the professionals from the incompetents. Good style in your programs will
improve your job prospects. Also good style in programs makes bugs
easier
to find and remove. Often using good style allows you to avoid bugs like
mismatched ()’s in the first place. Stylish programs will seem slower to
write but actually they will be faster since the style will force you to
understand what you are doing.
A program with panache is entirely obvious. Each word of the program should have an obvious meaning to the programmer and the reader of the program. The reason and necessity (or at least convenience) of each element of the program should be obvious. Style should make every word of the program so necessary that a reader could not even think of another way of writing the same program. Of course, this is an unachievable ideal but an attempt at an ideal leads to greatness.
In this document I will include two programs called poor.cpp and styled.cpp. poor.cpp is a program in the worst style that I could make myself write. styled.cpp has the best style I could easily write. The program with poor style compiles with only 1 minor warning but does not work and the bugs are quite subtle. The good style program worked perfectly the first time I tried it.
A program with panache will not cause any warnings (or of course) any errors when compiled. Originally usersNumber was an int which was not my intention. A warning from the compiler will often indicate a poorly thought out decision in your code. Always think about the code that is warned and only change it when you understand why the code was confusing and whether your clarification results in correct code.
|
|
const
int m = M; float
x[m]; int p = 2; |
|
|
float
sum = 0; // sum of the numbers in the circularArray for calculating average
float sumSquares = 0; // the sum of the squares of the #'s in circularArray for calculating variance |
| Lines that are too long can be broken if: | |
| You break them so that it is clear that they are incomplete. |
|
|
ResultVariable = ExoticFunction(x,MunicipalResult(y)
+ NuclearFactor(z); |
|
|
ResultVariable = ExoticFunction(x,MunicipalResult(y)
+
NuclearFactor(z); |
| You indent the rest of the line to indicate that it is part of the previous line. |
|
|
ResultVariable = ExoticFunction(x,MunicipalResult(y)
+
NuclearFactor(z); |
|
|
ResultVariable = ExoticFunction(x,MunicipalResult(y)
+
NuclearFactor(z); |
// This is the storage for the circular
array initialized to all 0's
static float
circularArray[circularArraySize] =
{ 0.0, 0.0, 0.0, 0.0, 0.0 };
// This function calculates the next
position in the circular array
inline int
next(int pos /* the position to update
*/)
Vertical alignment is used for larger or more complex
expressions like:
for
Avoid code where the brackets don’t line up, even if you
were copying your code from a professional source like a text book or the
help documents with your compiler. A common version of unaligned brackets
is:
void DoesNothing(void *PointerToNothing){
// this function does nothing with an unimportant pointer
}
|
|
int
obscure(int y)
|
|
|
int
obscure(int y)
|
|
|
do
|
|
|
do
|
|
|
for(iterator=0;iterator<size;iterator++)
// print and increment all elements of array
|
|
|
switch(userInput[0])
{ case ‘y’: case ‘Y’: // this is a positive answer return true; globalvariable++; ¬should be indented return false; ¬should be indented default: |
|
|
switch(userInput[0])
{ case ‘y’: case ‘Y’: // this is a positive answer case ‘N’: // this is a negative response } |
|
|
//
here we define a typical class it will describe a student
class student
protected: // this will contain
the data.
|
|
|
//
here we define a typical class it will describe a student
class student { protected: // this will contain the data. // returns the first name as a constant string const char *first_name() const |
// the % makes the array circulate
back to 0 when pos is circularArraySize -1
return (pos + 1)%circularArraySize;
is an example of obscure code that needs to be explained. Usually code is clearer than the above and what it does needs no further explanation.
In general you should not wait till you have completed the code before you comment it. Writing a comment on code makes you stop and think about what you are doing. The thinking allows you to avoid bugs before you write them so the coding would go faster. Also if you want to show your code to the instructor or to a friend, having comments will mean that you don’t have to explain each line.
/*
This program is meant to demonstrate
how a well written program can
make a complex idea clear.
Copyright David B. Sher 1998
*/
An example of variable commenting is:
|
|
const
int m = M; float ¬every
variable and constant should have its own line
x[m]; int p = 2; ¬every variable should have a comment |
|
|
float
sum = 0; // sum of the numbers in the circularArray for calculating average
float sumSquares = 0; // the sum of the squares of the #'s in circularArray for calculating variance int position = 2; // starting position in circular array |
Often a type is used to implement a metaphor. A type may implement a queue, which is a metaphor for a storage area where the first element that was entered is the first to be extracted, like a line for a bank teller. Such a type should have a comment that describes the metaphor for example:
/* a storage area where the first element
that was entered is the first to be extracted,
like a line for a bank
teller
only the queue functions should be used to modify or extract data from
this type
*/
typedef struct
{ float storage[max]; // storage area
for the floats
int first,last; // indices
of the first and late elements of the queue
int size; // the number of
elements in the queue
} Queue;
If you are commenting a complex type you should comment each part and express what each part does for the type. Explain how to use the type in the program Also discuss any caveats (limitations) on using the type. In the above example the last line of the code is a caveat.
// rubber baby bunker mice
int main()
Beyond that minimal level a comment should be sufficient so that a stranger can call your function without having to read the code, just the comment. Focus on the purpose of the function rather than how it works, for example:
// This function squares a number to
help calculate variance
inline float square(float number)
{ return number*number; }
Some examples of comments on loops are:
// Copies first N elements from array1
to array2
for(index=0;index<N;index++) array1[index]=array2[index];
// copies elements from linked list
into an array
for(linkPointer = list->next, index=0
; linkPointer != NULL ; linkPointer=linkPoint->next,index++)
array[index]=linkPointer->data;
if(list->next != null) // then the
list has no elements to act on
doSomething;
else // there are elements in the
list
It is usually necessary to examine each case in a switch statement and what it means. This comes up particularly often when parsing input:
// determine the command from the first
letter in the word
switch(command[0])
{
case ‘a’:
case ‘A’: // the activate commandActivate();
break; …
default: // can’t figure out which
command you want to execute cerr
<< "I do not understand " << command << endl;}
|
|
Array[x]
/= 2; ¬this
line needs a comment
cout << "Lets boogie\n" // prints a line which says lets boogie ¬this line doesn’t x++; // this line adds 1 to x ¬this comment doesn’t explain why |
|
|
size++; // after inserting
the element the size of the list will be 1 larger
cout << "Copyright David B. Sher 1998" |
The general rule is: variable names should be words or word combinations containing ordinary vowels and consonants. This allows you to talk about your program with professors, bosses, and colleagues. It is difficult to discuss how the variable mxtplztz changes during each iteration of a loop.
Avoid:
| Giving any variable the same name as a type. For example: float float; struct link link; | |
|
Giving a local variable the same name as a parameter (will
cause | |
| Giving a local variable the same name as a global variable. | |
| Giving two local variables the same name unless they do exactly the same thing. |
| Wrong | if(x>3,x<7) … // pointless test of x against 3 |
| Right | max(x,y) // , separates parameters |
| Right | for(int first=0,last=size-1; first<size; first+=1,last-=1) |
Important data structures like lists or trees should have their own header files and code files. Many programs can share the same data structures if they have their own files. Also interface code can often be shared among many programs if it is in separate files. Whenever you design a part of your program consider if you will probably use it in other programs. If so create a separate header and file for it.
Functions that do several tasks should call functions that do simple tasks. Often simple functions can be used in many parts of the program. Some functions should be dedicated to the user interface or interfacing with the file system. There should be a main function that calls these when necessary. The other functions should avoid any file system or user interaction. This allows these functions to be used many different user interfaces. Even error messages can be handled with throws.
If you organize your program this way you will have a more organized program and you will be able to reuse your carefully written code.
![]()
CopyRight David B. Sher 1996 Disclaimer
Number of visitors: 4098