Preface
Astute readers such as yourself may be wondering whether the title of this book, Safe C++ , presumes that the C++ programming language is somehow unsafe. Good catch! That is indeed the presumption. The C++ language allows programmers to make all kinds of mistakes, such as accessing memory beyond the bounds of an allocated array, or reading memory that was never initialized, or allocating memory and forgetting to deallocate it. In short, there are a great many ways to shoot yourself in the foot while programming in C++, and everything will proceed happily along until the program abruptly crashes, or produces an unreasonable result, or does something that in computer literature is referred to as unpredictable behavior. So yes, in this sense, the C++ language is inherently unsafe.
This book discusses some of the most common mistakes made by us, the programmers, in C++ code, and offers recipes for avoiding them. The C++ community has developed many good programming practices over the years. In writing this book I have collected a number of these, slightly modified some, and added a few, and I hope that this collection of rules formulated as one bug-hunting strategy is larger than the sum of its parts.
The undeniable truth is that any program significantly more complex than Hello, World will contain some number of errors, also affectionately called bugs. The Great Question of Programming is how we can reduce the number of bugs without slowing the process of programming to a halt. To start with, we need to answer the following question: just who is supposed to catch these bugs?
There are four participants in the life of the software program ():
The programmer
The compiler (such as g++ under Unix/Linux, Microsoft Visual Studio under Windows, and XCode under Mac OS X)
The runtime code of the application
The user of the program
Figure 1. Four participants (buggy version)
Of course, we dont want the user to see the bugs or even know about their existence, so we are left with participants 1 through 3. Like the user, programmer is human, and humans can get tired, sleepy, hungry, distracted by colleagues asking questions or by phone calls from family members or a mechanic working on their car, and so on. In short, humans make mistakes, the programmer is human, and therefore the programmer makes mistakes, a.k.a. bugs. In comparison, participants 2 and 3the compiler and the executable codehave some advantages: they do not get tired, sleepy, depressed, or burned out, and do not attend meetings or take vacations or lunch breaks. They just execute instructions and usually are very good at doing it.
Considering our resources we have to deal withthe programmer on the one hand, and the compiler and program on the otherwe can adopt one of two strategies to reduce the number of bugs:
Choice Number 1: Convince the programmer not to make mistakes. Look him in the eyes, threaten to subtract $10 from his bonus for each bug, or otherwise stress him out in the hopes to improve his productivity. For example, tell him something like this: Every time you allocate memory, do not forget to de-allocate it! Or else!
Choice Number 2: Organize the whole process of programming and testing based on a realistic assumption that even with the best intentions and most laserlike focus, the programmer will put some bugs in the code. So rather than saying to the programmer, Every time you do A, do not forget to do B, formulate some rules that will allow most bugs to be caught by the compiler and the runtime code before they have a chance to reach the user running the application, as illustrated in .
Figure 2. Four participants (happy/less buggy version)
When we write C++ code, we should pursue three goals:
The program should perform the task for which it was written; for example, calculating monthly bank statements, playing music, or editing videos.
The program should be human-readable; that is, the source code should be written not only for a compiler but also for a human being.
The program should be self-diagnosing; that is, look for the bugs it contains.
These three goals are listed in decreasing order of how often they are pursued in the real programming world. The first goal is obvious to everybody; the second, to some people, and the third is the subject of this book: instead of hunting for bugs yourself, have a compiler and your executable code do it for you. They can do the dirty work, and you can free up your brain energy so you can think about the algorithms, the designin short, the fun part.
Audience
If you have never programmed in C++, this book is not for you. It is not intended as a C++ primer. This book assumes that you are already familiar with C++ syntax and have no trouble understanding such concepts as the constructor, copy-constructor, assignment operator, destructor, operator overloading, virtual functions, exceptions, etc. It is intended for a C++ programmer with a level of proficiency ranging from near beginner to intermediate.
How This Book Is Organized
In Part I, we discuss the following three questions: in , we will examine the title question. Hint: its all in the family.
In , we will discuss why it is better to catch bugs at compile time, if at all possible. The rest of this chapter describes how to do this.
In and discuss the testing strategy that shows how to catch bugs in the most efficient manner.
We also discuss how to make your program debuggable. One of the goals when writing a program is to make it easy to debug, and we will show how our proposed use of error handling adds to our two friendscompiler and run-time codethe third one: a debugger, especially when it is working with the code written to be debugger-friendly.
And now we are ready to go hunting for actual bugs. In contain all necessary C++ files used in the book.
At this point you might be asking yourself, So instead of saying, When you do A, dont forget to do B were instead saying, When you do A, follow the rule C? How is this better? And are there more certain ways to get rid of these bugs? Good questions. First of all, some of the problems, such as memory deallocation, could be solved on the level of language. And actually, this one is already done. It is called Java or C#. But for the purposes of this book, we assume that for some reason ranging from abundant legacy code to very strict performance requirements to an unnatural affection for our programming language, were going to stick with C++.
Given that, the answer to the question of why following these rules is better than the old dont forget remonstrance is that in many cases the actual formulation of the rule is more like this:
The original: When you allocate memory here, do not forget to check all the other 20 places where you need to deallocate it and also make sure that if you add another return statement to this function, you dont forget to add a cleanup there too