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.

There are some qualities for programs such as clarity, readability, understandability, maintainability, as well as 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 (functions)

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.

On the other hand, a function should not be too short, either. Most often a function consisting only of a single line, or a function just calling another function, is too short. An exception is a getter method of a class returning the value of an attribute. Moreover, a 1-line function can be accepted if it improves readability, for example, the function below gives a title for a complicated condition:

bool is_snake_dead()
{
    return new_head.x < 0 or new_head.x >= width_ or
           new_head.y < 0 or new_head.y >= height_);
}

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.

Most often a name consisting only of a single letter is bad, but if the name is not essential, but a variable is e.g. an index, one letter can be enough, for example:

vector<int> lukuvektori;
...
for ( vector<int>::size_type i = 0; i < lukuvektori.size(); ++i )
{
    cout << lukuvektori.at(i) << endl;
}

Good names for index variables are i, j, k.

Typically, the names of variables and functions begin with a lower-case letter, and those of types, 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.

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.

First of all, each file starts with a comment describing the purpose of the whole file. Main program file has a header comment concerning the whole program.

Moreover, functions must be commented. The comment tells what the function does, what are the meaning and the valid values of its parameters and return value. This comment is written above the function. If there is a header file (.hh), the comment concerning a function is written in it, above the function.

Usually it is good to write a comment before each control structure to clarify its meaning. Especially, if a function consists of phases, these phases are commented. Moreover, if a code snippet is not obvious, or it is difficult to understand, the comment should tell what happens in it. Of course a better solution is to avoid code that is difficult to understand.

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.

Other style issues

Finally, some general style rules are listed shortly below. They 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.

  • 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 example

    int* ptr = nullptr;
    void fun( const vector<int>& vec );
    
  • The return value type of main is always int.

  • 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 a container as a parameter for a function, use a reference to the container, or a constant reference whenever possible (i.e. when there is no need to change the container).

  • 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 and comparison 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.)

  • The elements of a vector must, of course, share the same type, but in addition their meaning should be the same. For example, if you need store a person’s forename, surname, and address (all as strings), it is not a good idea to use a vector, but a struct.

  • 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 not be considered on this 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 classes and object-oriented programming. We will return to them in the context of these topics.

Wish

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.