C++
is a language where new and different features are built on top of an existing
syntax. (Because of this it is referred to as a
hybrid
object-oriented programming language.) As more people have passed through the
learning curve, we’ve begun to get a feel for the way C programmers move
through the stages of the C++ language features. Because it appears to be the
natural progression of the C-trained mind, I decided to understand and follow
this same path, and accelerate the process by posing and answering the
questions that came to me as I learned the language and that came from
audiences as I taught it.
This
course was designed with one thing in mind: the way people learn the C++
language. Audience feedback helped me understand which parts were difficult and
needed extra illumination. In the areas where I got ambitious and included too
many features all at once, I came to know – through the process of
presenting the material – that if you include a lot of new features, you
have to explain them all, and the student’s confusion is easily
compounded. As a result, I’ve taken a great deal of trouble to introduce
the features as few at a time as possible; ideally, only one at a time per
chapter.
The
goal, then, is for each chapter to teach a single feature, or a small group of
associated features, in such a way that no additional features are relied upon.
That way you can digest each piece in the context of your current knowledge
before moving on. To accomplish this, I leave many C features in place much
longer than I would prefer. For example, I would like to be using the C++
iostreams IO library right away, instead of using the
printf( )
family of functions so familiar to C programmers, but that would require
introducing the subject prematurely, and so many of the early chapters carry
the C library functions with them. This is also true with many other features
in the language. The benefit is that you, the C programmer, will not be
confused by seeing all the C++ features used before they are explained, so your
introduction to the language will be gentle and will mirror the way you will
assimilate the features if left to your own devices.
Here
is a brief description of the chapters contained in this book [[ Please note
this section will not be updated until all the chapters are in place ]]
(0)
The evolution of objects.
When projects became too big and too complicated to easily maintain, the
“software crisis” was born, saying, “We can’t get
projects done, and if we can they’re too expensive!” This
precipitated a number of responses, which are discussed in this chapter along
with the ideas of object-oriented programming (OOP) and how it attempts to
solve the software crisis. You’ll also learn about the benefits and
concerns of adopting the language and suggestions for moving into the world of
C++.
(1)
Data abstraction.
Most features in C++ revolve around this key concept: the ability to create new
data types. Not only does this provide superior code organization, but it lays
the ground for more powerful OOP abilities. You’ll see how this idea is
facilitated by the simple act of putting functions inside structures, the
details of how to do it, and what kind of code it creates.
(2)
Hiding the implementation.
You
can decide that some of the data and functions in your structure are
unavailable to the user of the new type by making them
private.
This means you can separate the underlying implementation from the interface
that the client programmer sees, and thus allow that implementation to be
easily changed without affecting client code. The keyword
class
is also introduced as a fancier way to describe a new data type, and the
meaning of the word “object” is demystified (it’s a variable
on steroids).
(3)
Initialization & cleanup.
One
of the most common C errors results from uninitialized variables. The
constructor
in C++ allows you to guarantee that variables of your new data type
(“objects of your class”) will always be properly initialized. If
your objects also require some sort of cleanup, you can guarantee that this
cleanup will always happen with the C++
destructor.
(4)
Function overloading & default arguments.
C++
is intended to help you build big, complex projects. While doing this, you may
bring in multiple libraries that use the same function name, and you may also
choose to use the same name with different meanings within a single library.
C++ makes this easy with
function
overloading
,
which allows you to reuse the same function name as long as the argument lists
are different. Default arguments allow you to call the same function in
different ways by automatically providing default values for some of your
arguments.
(5)
Introduction to 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.
(6)
Constants.
This
chapter covers the
const
and
volatile
keywords that have additional meaning in C++, especially inside classes. It
also shows how the meaning of
const
varies inside and outside classes and how to create compile-time constants in
classes.
(7)
Inline functions.
Preprocessor macros eliminate function call overhead, but the preprocessor also
eliminates valuable C++ type checking. The inline function gives you all the
benefits of a preprocessor macro plus all the benefits of a real function call.
(8)
Name control.
Creating
names is a fundamental activity in programming, and when a project gets large,
the number of names can be overwhelming. C++ allows you a great deal of control
over names: creation, visibility, placement of storage, and linkage. This
chapter shows how names are controlled using two techniques. First, the
static
keyword is used to control visibility and linkage, and its special meaning with
classes is explored. A far more useful technique for controlling names at the
global scope is C++’s
namespace
feature, which allows you to break up the global name space into distinct
regions.
(9)
References & the copy-constructor.
C++ pointers work like C pointers with the additional benefit of stronger C++
type checking. There’s a new way to handle addresses; from Algol and
Pascal, C++ lifts the
reference
which lets the compiler handle the address manipulation while you use ordinary
notation. You’ll also meet the copy-constructor, which controls the way
objects are passed into and out of functions by value. Finally, the C++
pointer-to-member is illuminated.
(10)
Operator overloading.
This feature is sometimes called “syntactic sugar.” It lets you
sweeten the syntax for using your type by allowing operators as well as
function calls. In this chapter you’ll learn that operator overloading is
just a different type of function call and how to write your own, especially
the sometimes-confusing uses of arguments, return types, and making an operator
a member or friend.
(11)
Dynamic object creation.
How many planes will an air-traffic system have to handle? How many shapes will
a CAD system need? In the general programming problem, you can’t know the
quantity, lifetime or type of the objects needed by your running program. In
this chapter, you’ll learn how C++’s
new
and
delete
elegantly solve this problem by safely creating objects on the heap.
(12)
Inheritance & composition.
Data abstraction allows you to create new types from scratch; with composition
and inheritance, you can create new types from existing types. With composition
you assemble a new type using other types as pieces, and with inheritance you
create a more specific version of an existing type. In this chapter
you’ll learn the syntax, how to redefine functions, and the importance of
construction and destruction for inheritance & composition.
(13)
Polymorphism & virtual functions.
On your own, you might take nine months to discover and understand this
cornerstone of OOP. Through small, simple examples you’ll see how to
create a family of types with inheritance and manipulate objects in that family
through their common base class. The
virtual
keyword allows you to treat all objects in this family generically, which means
the bulk of your code doesn’t rely on specific type information. This
makes your programs extensible, so building programs and code maintenance is
easier and cheaper.
(14)
Templates & container classes.
Inheritance
and composition allow you to reuse object code, but that doesn’t solve
all your reuse needs. Templates allow you to reuse
source
code by providing the compiler with a way to substitute type names in the body
of a class or function. This supports the use of
container
class
libraries,
which are important tools for the rapid, robust development of object-oriented
programs. This extensive chapter gives you a thorough grounding in this
essential subject.
(15)
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.
(16)
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.
(17)
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 it is very 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.
AppendixA:
Etcetera.
At
this writing, the C++ Standard is unfinished. Although virtually all the
features that will end up in the language have been added to the standard, some
haven’t appeared in all compilers. This appendix briefly mentions some of
the other features you should look for in your compiler (or in future releases
of your compiler).
Appendix
B: Programming guidelines
.
This appendix is a series of suggestions for C++ programming. They’ve
been collected over the course of my teaching and programming experience, and
also from the insights of other teachers. Many of these tips are summarized
from the pages of this book.
Appendix
C:
Simulating
virtual constructors
.
The constructor cannot have any virtual qualities, and this sometimes produces
awkward code. This appendix demonstrates two approaches to “virtual
construction.”