Today we are going to talk about something a little different than what we have talked about so far in our object-oriented programming series. We will discuss some common mistakes programmers make when writing C++ programs, where to look when your program isn't working correctly (or isn't working at all), and how to use some of the debugging tools available in most IDEs. Before we begin, it should be clear that every problem is specific, and there are no universal rules or ways to debug your program. However, if we consider the usual, object-oriented structure of a C++ program, we can implement some basic strategies for identifying and fixing errors in our program.
First, let's make a distinction between various types of errors which can occur:
- Compiler errors: These errors (and sometimes warnings) are detected by the compiler. They prevent the program from being compiled. Since today's compilers are extremely complex, they can detect a large variety of mistakes, including syntax errors, wrong number of arguments to a function, undefined references etc.
- Linker errors: These are errors generated when the executable of the program cannot be generated. This may be due to wrong function prototyping or incorrect header files.
- Execution errors: This is a large class of various errors which went undetected by the compiler. In C++, you will often get errors like segmentation faults, which mean that the program tried to access a part of memory which didn't "belong" to it. This is due to the nature of C++: constant dealing with pointers and references and being responsible for memory management yourself.
This leads to some common mistakes, such as:
- Not freeing all of the dynamically allocated memory
delete instead of
- Returning local objects by reference
- Using references to deleted resources
This list is just something which should be checked when debugging your programs; these mistakes are easily made, and sometimes they can be difficult to find.
Now, we will talk about debuggers: collections of very powerful tools which IDEs offer to the programmer. The debugger enables you to follow the execution path of your program, and examine the value of variables as you step through your code. This is extremely valuable when you are trying to determine why your program doesn't work.
The most significant technique for debugging is strategically placing breakpoints: this enables the programmer to intentionally pause the execution of the program, in order to inspect variables, memory, logs, files etc. Placing breakpoints at key points in the program is a good strategy for locating an error. Here is an example of debugging a program in C++, using Visual Studio (most IDEs have very similar ways of placing breakpoints, inspecting memory etc).
Note that you have to set your program to "debug" mode in order for this to work. As you can see in the picture, after you've placed a breakpoint and ran the program, you can inspect values of variables simply by hovering over them. You can also use the "Immediate Window" to evaluate expressions during debugging.