Выбрать главу

Thinking In C++. Volume 2: Practical Programming

To all those who have worked tirelessly on the development of the C++ language

Preface

In Volume 1 of this book, you learn the fundamentals of C and C++. In this volume, we look at more advanced features, with an eye towards developing techniques and ideas that produce robust C++ programs.

Thus, in this volume we are assuming that you are familiar with the material developed in Volume 1.

Goals

Our goals in this book are to:.

1. Present the material a simple step at a time, so the reader can easily digest each concept before moving on.

2. Teach «practical programming» techniques that you can use on a day-to-day basis.

3. Give you what we think is important for you to understand about the language, rather than everything we know. We believe there is an «information importance hierarchy,» and there are some facts that 95% of programmers will never need to know, but that would just confuse people and add to their perception of the complexity of the language. To take an example from C, if you memorize the operator precedence table (we never did) you can write clever code. But if you have to think about it, it will confuse the reader/maintainer of that code. So forget about precedence, and use parentheses when things aren’t clear. This same attitude will be taken with some information in the C++ language, which is more important for compiler writers than for programmers.

4. Keep each section focused enough so the lecture time—and the time between exercise periods—is small. Not only does this keep the audience’ minds more active and involved during a hands-on seminar, but it gives the reader a greater sense of accomplishment.

5. We have endeavored not to use any particular vendor’s version of C++. We have tested the code on all the implementations we could, and when one implementation absolutely refused to work because it doesn’t conform to the C++ Standard, we’ve flagged that fact in the example (you’ll see the flags in the source code) to exclude it from the build process.

6. Automate the compiling and testing of the code in the book. We have discovered that code that isn’t compiled and tested is probably broken, so in this volume we’ve instrumented the examples with test code. In addition, the code that you can download from http://www.MindView.net has been extracted directly from the text of the book using programs that also automatically create makefiles to compile and run the tests. This way we know that the code in the book is correct.

Chapters

Here is a brief description of the chapters contained in this book:

Part 1: Building Stable Systems

1. Exception handling. Error handling has always been a problem in programming. Even if you dutifully return error information or set a flag, the function caller may simply ignore it. Exception handling is a primary feature in C++ that solves this problem by allowing you to «throw» an object out of your function when a critical error happens. You throw different types of objects for different errors, and the function caller «catches» these objects in separate error handling routines. If you throw an exception, it cannot be ignored, so you can guarantee that something will happen in response to your error. The decision to use exceptions (a good one!) affects code design in fundamental ways.

2. Defensive Programming. Many software problems can be prevented. To program defensively is to craft code in such a way that bugs can be found and fixed early before they have a chance to do damage in the field. The use of assertions is the single most important thing you can do to validate your code during development, while at the same time leaving an executable documentation trail in your code that reveals what you were thinking when you wrote the code in the first place. Before you let your code out of your hands it should be rigorously tested. A framework for automated unit testing is an indispensable tool for successful, everyday software development.

Part 2: The Standard C++ Library

3. Strings in Depth. Text processing is the most common programming activity by far. The C++ string class relieves the programmer from memory management issues, while at the same time delivering a powerhouse of text processing capability. C++ also supports the use of wide characters and locales for internationalized applications.

 4. Iostreams. One of the original C++ libraries—the one that provides the essential I/O facility—is called iostreams. Iostreams is intended to replace C’s stdio.h with an I/O library that is easier to use, more flexible, and extensible—you can adapt it to work with your new classes. This chapter teaches you the ins and outs of how to make the best use of the existing iostream library for standard I/O, file I/O, and in-memory formatting.

5. Templates in Depth. The distinguishing feature of «modern C++» is the broad power of templates. Templates are for more than just generic containers; they support development of robust, generic, high-performance libraries. There is a lot to know about templates—they constitute, as it were, a sub-language within the C++ language, and give the programmer an impressive degree of control over the compilation process. It is not an understatement to say that templates have revolutionized C++ programming.

6. Generic Algorithms. Algorithms are at the core of computing, and C++, through its template facility, supports an impressive entourage of powerful, efficient, and easy-to-use generic algorithms. The standard algorithms are also customizable through function objects. This chapter looks at every algorithm in the library. (Chapters 6 and 7 cover that portion of the standard C++ library commonly-known as the Standard Template Library, or STL.)

7. Generic Containers & Iterators. C++ supports all the common data structures known to man in a type-safe manner. You never have to worry about what such a container holds; the homogeneity of its objects is guaranteed. Separating the traversing of a container from the container itself, another accomplishment of templates, is made possible through iterators. This ingenious arrangement allows a flexible application of algorithms to containers by means of the simplest of designs.

Part 3: Special Topics

8. Run-time type identification. Run-time type identification (RTTI) lets you find the exact type of an object when you only have a pointer or reference to the base type. Normally, you’ll want to intentionally ignore the exact type of an object and let the virtual function mechanism implement the correct behavior for that type. But occasionally (like when writing software tools such as debuggers) it is helpful to know the exact type of an object for which you only have a base pointer; often this information allows you to perform a special-case operation more efficiently. This chapter explains what RTTI is for and how to use it.

9. Multiple inheritance. This sounds simple at first: A new class is inherited from more than one existing class. However, you can end up with ambiguities and multiple copies of base-class objects. That problem is solved with virtual base classes, but the bigger issue remains: When do you use it? Multiple inheritance is only essential when you need to manipulate an object through more than one common base class. This chapter explains the syntax for multiple inheritance, and shows alternative approaches—in particular, how templates solve one common problem. The use of multiple inheritance to repair a «damaged» class interface is demonstrated as a genuinely valuable use of this feature.