About programming style¶
C++ allows you write very cryptic code, but such code should be avoided. It is better to write clear code that is easy to read and understand. This also makes program maintenance easier.
Above text mentioned some qualities for programs (clarity, readability, understandability, maintainability). Other important things are functionality and efficiency. Such qualities are difficult to define, and there is not a single way to follow a good programming style.
Style guidance followed on this course is described below, and the items are those that also the assistants pay attention to, when they evaluate projects. However, the purpose is to give as general rules as possible to make them useful more widely than just on this course.
Functionality¶
Functionality of a program can be seen as the most important quality. But if the program works correctly, does it matter at all how the code looks like or how easy it is to understand? However, when the structure of the program is logical and as simple as possible, the program more probably works correctly or you can more easily make it work correctly.
The list below gives some requirements for functionality:
- The program must work correctly with all valid input values.
- The program must inform the user about erroneous or invalid input.
- The program must inform the user about a wrong way to use the program.
- The program must check the input values to prevent crashing. Instead of crashing it must quit in a controlled manner.
- The program must not ask such values that it can calculate itself.
- Using the program must be as easy and clear as possible.
Structuring a program¶
Structuring a program means a way to divide the program into parts. Such parts should be small and simple enough and easy to manage.
Structuring concerns any program part, e.g. functions and classes.
Especially functions must not be too long nor too wide (the lines must not be too long). A function should fit on the screen in total.
Repetitive code should be avoided. This means writing the same or almost the same code several times. You can avoid this e.g. by functions or by giving additional parameters to functions.
Naming conventions¶
C++ is a case-sensitive programming language, which means that
upper-case and lower-case letters are treated as distinct.
Although it would be possible to use identifiers name
and Name
in the same scope, such a convention does not follow good programming style.
More generally, it is better to avoid using names that are too similar,
since you can confuse them easily.
The names of variables, constants, functions, types, classes and other identifiers must be descriptive. It does not matter if the names are long.
Typically, the names of variables and functions begin with a lower-case letter, and those of classes and structs with an upper-case letter. Constants are written with upper-case letters in total and different words are separated with an underline.
There are mainly two ways to name a function:
printField
and print_field
.
The former is better, but anyway you must follow
the chosen way consistently in a program.
The names of functions should be commands such as print
, instead
of nouns such as printing
.
Functions returning a truth value (bool
) are recommended to start with
the string is
.
In such case, you can conclude from the name, in which situations the
function will return true
and in which situations false
.
Therefore isReady
is a better name for a truth-value function
than checkStatus
.
However, it is possible that a function returns a truth value to inform
whether the implemented operation succeeded or not.
In such case, it is not necessary to follow the above naming rule.
For example, the main purpose of function setValue
is to set a value,
but in addition it can return a truth value: true
indicating a succeeded
operation and false
a failed one.
The names of classes should be in singular form rather in plural, since a class typically describes a single object.
It is a common practise that the last character of a member variable is the underline. This makes it easier to distinguish them from other variables.
It is better to use named constants instead of magic numbers.
It is easier to read program code, if there appears e.g. the constant
NUMBER_OF_STUDENTS
instead of the number 600
.
Named constants are also useful, if you want to change its value:
It is enough to edit a single point in the code.
Like other identifiers, also constants must have names that
describe their use, so it is not reasonable to define:
const int HUNDRED = 100
.
Comments¶
Comments can improve the understandability of the program. On the other hand, understandability can also be improved by giving descriptive names for variables and other identifiers. The following example can be found from Steve McConnell’s book Code Complete (p. 473):
// if allocation flag is zero
if( AllocFlag == 0 ) ...
The comment above is totally useless, because you can get the same information from the code. The comment can be improved as:
// if allocating new member
if( AllocFlag == 0 ) ...
A better way is to use a named constant instead of zero:
// if allocating new member
if( AllocFlag == NEW_MEMBER ) ...
By the way, the comment is again useless (which is now a good thing, and the comment can be removed).
When commenting code you should stop to think if the comment is useful. However, at this phase when you are only learning programming, it is better to write too much comments than too less of them.
Functions must be commented.
Especially the comments concerning a member function are written
in the header file (.hh
) before the function.
The comment tells what the function does, what are the meaning and
the valid values of its parameters and return value.
Each file starts with a comment describing the purpose of the whole file. Usually it is good to write a comment before each control structure to clarify its meaning.
Code layout¶
Although C++ does not require indentations, use them to improve the readability of the code.
The body of control structures must be enclosed with curly brackets,
i.e. { }
, even if the body consists of a single statement.
Curly brackets can be written in the lines of their own,
or the starting curly bracket, i.e. {
, can be written at the end of the
preceding line.
You can use either of the above ways, but use the chosen way consistently
in the whole program.
Object-oriented programming¶
The style issues of this section are related to classes and object-oriented programming.
Style issues in the current and following sections have been taken from the Finnish book ”Olioiden ohjelmointi C++:lla” written by Matti Rintala and Jyke Jokinen (pp. 341-353). Most of the points have been mentioned in other parts of the material with larger explanation. Therefore they are listed here shortly.
Each header file (
.hh
) contains a single public interface (class). Header files contain only definitions (no implementations).Header files must be prevented from including more than once:
#ifndef CLASS_A_HH #define CLASS_A_HH ... #endif // CLASS_A_HH
include
directives can be used to include only header files. If you include several files, you must list your own files first and after that the library files.The implementation of an interface is written in the implementation file (
.cpp
) in the same order as they are in the corresponding header file (.hh
).Classes have been defined with a good public interface (as small as possible), and all the operations are conducted via this interface.
- Especially, an object must not change the values of another object’s attributes, not even if the objects are instances of the same class.
Write the
public
part of a class before theprivate
one.The
public
part of a class contains no member variables.The
private
part of a class contains no useless variables.No operations of a class are implemented outside the member functions.
The member variables of a class must be initialized in their definition or in the initialization list of the constructor in the same order as they are defined in the header file.
When possible, define a member function as a
const
function.
Other style issues¶
Finally, some general style rules are listed shortly below.
The scope of a variable must be as small as possible (e.g block, function, class).
Each variable must be defined separately, and thus, avoid writing definitions as
int a, b;
.Variables must be initialized.
The characters for pointers and references (
*
and&
) are written immediately after the type without an empty space. For exampleDate* ptr = nullptr; void printDate( Date& dateObject );
The return value type of
main
is alwaysint
.The names of the parameters of a function must be given in both the declaration and definition of a class, and they must be identical in both places.
If you want to pass an object as a parameter for a function, use a reference to the object instead the object itself, whenever possible.
The default value parameters of a function must be given in the declaration of the function, and you must not give more of them in the definition of the function.
A function must not return a reference or a pointer to its local data.
Records (
struct
) must not have member functions, but constructors, an (empty) destructor, and operator functions are allowed.Do not index vectors with brackets, but use
at
operation instead. (The same holds also for other containers that have these operations defined.)Avoid unnecessary complexity. For example, it is useless to evaluate a boolean expression, if you can return its value directly. As a concrete example, the function
bool is_negative(int number) { if(number < 0) { return true; } else { return false; } }
can simply be written as:
bool is_negative(int number) { return number < 0; }
(which makes one in this case wonder, if the function is needed at all.)
Unless you are absolutely sure that you can use C++ exception mechanism, then do not try to use it. Exception handling in C++ will be considered only on the next programming course. Especially, C++ exception mechanism must not be used in the place of control structures (as you may have done in Python programs).
C++ has other style guidelines e.g. those related to dynamic memory management and inheritance of classes. We will return to them in the context of these topics.
Attention
We have tried to follow the above guidelines in the template codes and examples of the course. However, we have not always succeeded in doing so, since there have been many authors writing the codes and most often they have worked in a hurry. We hope that you inform us if you notice any violations of the guidelines.