C++ Program

Style Rules

 

Copyright 2003-2009 Dr. Clinton Staley

clintstaley@gmail.com

 

The following style rules are mandatory for all programs submitted.  Breaking a style rule will cause your program to “bounce”, and you will have to redo the code according to correct style.

 

1. Identifier names

A. Choose names carefully.  X, jj, and b2 don't mean as much as timeLeft, biggest, and whereNext.  Avoid single letter variable names, except where they are clearly explanatory (e.g. as "x" and "y" in coordinate geometry.)

 

B. If you decide to abbreviate a commonly used term in variable or type names, use exactly the same abbreviation everywhere.  (For instance, if the term "category" appeared often in variable or type names, you might abbreviate it everywhere to "ctg".)

 

C. In multiple-word names, capitalize the first letter of the second and later words.  Do not use underscores.  In names of functions, classes, types, and global data, capitalize the first letter of the entire variable name, too.  Leave the first letter lowercase in locals and member data.

 

Functions, globals, classes, types:  String   ClearList  TotalSize

 

Locals and member data: stringLength  nameNdx  bestScore

 

D. Do not use “Hungarian notation”.  However, do add an "m" ahead of each member datum name.  (Member data names might be mCount, mCurrentItems, etc.)  This helps avoid bugs due to accidentally naming a local variable and a member datum identically.  And add a "k" in front of each constant (e.g. kMaxSize, kArraySize).

 

2. Constants

Use defined constants.   There should be no actual numbers other than 0, 1, -1 or 2 in your code.  Even these might sometimes be constants if they could change in later versions of the code.  Use standard (rule 1) variable naming conventions for constants.  Do not use #defines; use const.

 

Declare parameters, variables, return values, and member functions to be const wherever possible.  Use const references when passing data of over 4 bytes by value.   When you pass data “by reference”, so that the function may modify the actual parameter, do not use references.  Instead, use pointers in the same way you would in ANSI C.

 

3. Global Variables

Never use a non-const global variable without first asking me.  And if you ask me, I will say “No.” 

 

4. Function Design

A. Functions must be at most 50 lines long, not counting blank lines, assertions, or lines containing only a single brace.  Don't cram code to satisfy this limit.  Break up your functions instead.  Functions with long switch statements or if-else blocks are an exception to this rule.

 

B. Always put local variables at the top of the function, not interspersed in the code, unless the program requires delayed construction of the local.

 

 

5. Class Design

A. When a type is just a simple set of data, use the keyword struct.  A struct may have a constructor and destructor, but would rarely have any other methods.  If you need more methods, use the class keyword, and...

 

B. Never use a public data member except in structs as defined in 5A.  If you have a member that you would like to make freely modifiable, make the data member protected, and create two public member functions to access and modify it, using the same name as the data member, but with “Set” and “Get” added.  This arrangement lets you attach more complex code to the accessing and modifying of the data in the future.

 

class Type {

public:

   int  GetData()        {return mData;}  // Get data value

   void SetData(int val) {mData = val;}   // Modify

protected:

   int mData;

};

 

C.  In the .cpp implementation of a class, define the member functions in the same order that they were listed in the .h class declaration.

 

D. Classes that cannot be properly copied by memberwise assignment (such as those with dynamically allocated storage) must have a copy constructor, virtual destructor, and operator= function.  Do this even if you never plan to use these functions.  These three functions must either be correctly implemented and tested, or be placed in the private section with "assert(0)" as a body, so that accidental use will be flagged either by the compiler or the assertion.

 

Where possible, implement these three member functions by creating two private member functions Delete and Duplicate, which perform destruction of the class, and copy construction, respectively.  The destructor and copy constructor simply call Delete and Copy, respectively.  The operator= function calls them both -- first Delete and then Copy.

 

E. Include inside the class (publicly or privately) all types that the class needs for its internal data and for its parameters.  Don’t declare such types outside of the class.

 

F. Use inline member functions, defined inside the class declaration, whenever the function is short enough to fit on just one line, and does not rely on other class declarations (see item 5H).   You may place multiple statements per line in this case, but obey horizontal spacing rules (see 8).  If a method is not inline, make it virtual.

 

G. Destructors in classes (not necessarily structs) must be virtual.

 

H. When a header file A.h for class A includes pointers or references to class B, do not include B.h in A.h.  Such inclusions result in tightly interdependent code with attendant long builds.  Instead, use forward declarations (e.g. "class B;").  Do not put inline functions in A.h if they result in the need to include B.h unless such inline code is essential for efficiency.

 

 

6. Indentation and Blank Lines

A. Use 3 space indentation, and indent only one level for each nested  if, while, or switch.  Do not use tabs for indentation.   Hidden tabs are one of the most common sources of style bounces.  You may want to use the "detab" program from my ~cstaley/bin directory.

 

   while (...) {

      statement;

      if (...) {

         statement;

      }

   } 

 

B. Never let a line exceed 80 columns.  If a line must be broken into two, indent the second part one space past the first column of the first part:

 

       Big long line....

        with continuation line below

 

C. Indent both the then and else blocks of an if-statement, even if  they are only one line long.  Don't write: if (test) statement;

 

D. Use blank lines to break up blocks of code.  Code should fall into groups of about 5 lines on average, separated by a blank line or a line with only a brace.  Always put a blank line after the local declarations in a function.   Don’t put more than one blank line in a row.  A single blank line at a time is enough to break up the flow of code properly.

 

E. Place the opening brace at the end of the first line of the if, while, struct, etc. that it applies to.  (See examples under 6A and 11.).  The only exception is the opening brace of a function, which goes on a line by itself.

 

F. Indent member functions and member data 3 spaces relative to the "class" or "struct" keyword, but do not indent the keywords “private”, “public", or "protected".

 

struct {

   int Add();

protected:

   float total;

};

 


7. Large Scale Organization

A. Files should be at most 600 lines long.

 

B. Include files must be protected by #ifndef/#endif pairs or #pragma once, to avoid  multiple inclusion. 

 

C. Files should come in .h/.cpp pairs, with each pair defining one class.  (e.g. string.h and string.cpp define a String class.)   If two classes are friends, they can go in the same .h/.C file pair, but otherwise it’s one class per file pair.   Do not cram declarations, constants, and classes into a big, general purpose, .h file.

 

D. If a declaration is needed in only one .cpp file, don't put it in a .h file at all -- include it in the .cpp file that needs it.

 

E. Do not use using namespace in a .h file, since this forces all includers of the header to use that namespace.  Use full namespace scope in .h files. Use using namespace only in .cpp files.

 

F. Avoid excessive .h file dependencies.  In particular, do not include one non-system .h file in another when a forward declaration will suffice.  Ensure that forward declarations will generally suffice by avoiding inline methods that make use of methods or member data of other classes.

 

8. Horizontal Whitespace

Use whitespace to clarify your code and to break up long expressions, but don't overdo it.

 

A. Put spaces after each comma, and around each keyword (note that "if", "while" and "for" are keywords).  Put a space after each semicolon in a for header.  Never put a space before a comma or semicolon.

 

B. Put spaces around operators, except for operators on the top two rows of the precedence table (e.g. [], ++, &) or in very large expressions where you may avoid spaces around the innermost operators.  Don’t have more than three variables or operators in a row without a blank space.

 

Good:  epsilon = 2*beta[1] + *gamma - delta*pi;

Bad:   epsilon=2**gamma-delta*pi;

 

C. Put single blank lines after each function, and between local declarations and the function code. 

 

D. Don't put space after an opening paren, or before a closing one.  Do not put space between a function name and the opening paren for the parameter list.

 

 

9. Comments

Be sure the main file includes a comment giving your name, and section number if you're in a multi-section course.

 

Add declaration comments to each header file.   Declaration comments describe the class or classes declared in the header, including each member function and parameter, and each member datum.  Declaration comments describe only the interface, not implementation details.  (Member data are technically an implementation detail, but since they reside in the header file, we document them in the declaration comment.)  

 

Add implementation comments to each source file, describing how each function in the source file works.  Do not repeat information from the declaration comments in the implementation comments; stick strictly to describing the implementation.

 

Don't comment the obvious; concentrate on explaining the hard parts.  Put variable, type, and function names in quotes in comments, to distinguish them from ordinary words.  Any time a method or implementation is changed, the comment must be changed as well.  Keeping comments current is essential to their usefulness.  If comments frequently are out of date, people start to ignore all comments.

 

Comments must be clear.   A set of words in comment markers is not automatically a comment.  If the comment isn't clear,  it doesn't exist.  Comments must be perfectly spelled, and perfectly grammatical.   We routinely bounce programs for grammar and spelling errors.

 

Most professional programmers do not like to see comments intermingled line-by-line with code, since this distracts their eye as they read the code.  Put comments in function headers, not in the code itself.  You may mark a line with a number thus: // 1 , and discuss it in the function header.  On rare occasions you may add a short comment to the right of the code to explain something especially confusing.

 

 

10. Code Safety

The following rules will help eliminate coding errors, or make them easier to catch:

 

A.  Use assertions to check input assumptions on functions and to check the outcome of complex blocks of code.  Assertions should only be used to check for bugs in the code, not to cover user or file format errors.  Use exceptions for these.   Write an assertion only if you would change the code in the event of the assertion’s failure.  As a rule of thumb, you should have 3-8 assertions per page of code.

 

B.  Always add a “default” case to a switch.  If this case should never be reached, then place an “assert(0)” in it.

 

C. When you delete data, immediately set its pointer to 0.  This prevents double-deletion or accidental use of deleted data.  YIn this case, you may put the 0-assignment on the same line as the deletion:

 

    delete ptr; ptr = 0;

 

D. Initialize all member data of a class in the constructor, even data you expect to reassign later. 


11. Style Examples

Example of Good Style:

 

int test(int number)

{

   int count;

 

   for (count = 1; count < MAX; count++) {

      if (number+1 > i && number-1 < count) {

         cout << "hello" << endl;

      }

      else {

         cout << "goodbye" << endl;

      }

      number = number + 1;

   }

 

   count = number;

   while (count > MAX)

      count = count - DECREMENT;

}

 

Example of Bad Style:

int test(int n) {          <-- { on same line as function header

   int i;                  <-- one letter name, no space after locals

   for (i=1;i<5;n=n+1,i++) <-- bad horizontal space, use of constant       

   {                       <-- belongs on prior line

      if (n+1>i && n-1 < i) <-- Single letter variable names.

/*********************************  <-- Dumb banner comment

 *  This is an if statement !    *

 *********************************/

         {                 <-- Double indentation

            cout << "hello" << endl;

         }

      else {            

            cout << "goodbye" << endl;

         }

   }

   i = n;

   while(i > 10)           <-- no space after "while", use of constant

      i = i - 2;

}