Both
the
Stash
and
Stack
classes have a function called
initialize( ),
which hints by its name that it should be called before using the object in any
other way. Unfortunately, this means the client programmer must ensure proper
initialization. Client programmers are prone to miss details like
initialization in their headlong rush to make your amazing library solve their
problem. In C++, initialization is too important to leave to the client
programmer. The class designer can guarantee initialization of every object by
providing a special function called the
constructor.
If a class has a constructor, the compiler automatically calls that constructor
at the point an object is created, before client programmers can get their
hands on the object. The constructor call isn’t even an option for the
client programmer; it is performed by the compiler at the point the object is
defined.
The
next challenge is what to name this function. There are two issues. The first
is that any name you use is something that can potentially clash with a name
you might like to use as a member in the class. The second is that because the
compiler is responsible for calling the constructor, it must always know which
function to call. The solution Stroustrup chose seems the easiest and most
logical: The name of the constructor is
the same as the name of the class. It makes sense that such a function will be
called automatically on initialization.
Here’s
a simple class with a constructor:
class X {
int i;
public:
X(); // Constructor
};
Now,
when an object is defined,
void f() {
X a;
// ...
}
the
same thing happens as if
a
were an
int:
Storage is allocated for the object. But when the program reaches the
sequence
point
(point
of execution) where
a
is defined, the constructor is called automatically. That is, the compiler
quietly inserts the call to
X::X( )
for the object
a
at the point of definition. Like any member function, the first (secret)
argument to the constructor is the
this
pointer
– the address of the object for which it is being called. In the case of
the constructor, however,
this
is pointing to an un-initialized block of memory, and it’s the job of the
constructor to initialize this memory properly.
Like
any function, the constructor can have arguments
to allow you to specify how an object is created, give it initialization
values, and so on. Constructor arguments provide you with a way to guarantee
that all parts of your object are initialized to appropriate values. For
example, if the class
Tree
has a constructor that takes a single integer argument denoting the height of
the tree, then you must create a tree object like this:
Tree
t(12); // 12-foot tree
If
tree(int)
is your only constructor, the compiler won’t let you create an object any
other way. (We’ll look at multiple constructors and different ways to
call constructors in the next chapter.)
That’s
really all there is to a constructor: It’s a specially named function
that is called automatically by the compiler for every object, at the point of
that object’s creation. Despite it’s simplicity, it is
exceptionally valuable because it eliminates a large class of problems and
makes the code easier to write and read. In the preceding code fragment, for
example, you don’t see an explicit function call to some
initialize( )
function that is conceptually separate from definition. In C++, definition and
initialization are unified concepts – you can’t have one without
the other.
Both
the constructor and destructor are very unusual types of functions: They have
no return value.
This is distinctly different from a
void
return value, where the function returns nothing but you still have the option
to make it something else. Constructors and destructors return nothing and you
don’t have an option. The acts of bringing an object into and out of the
program are special, like birth and death, and the compiler always makes the
function calls itself, to make sure they happen. If there were a return value,
and if you could select your own, the compiler would somehow have to know what
to do with the return value, or the client programmer would have to explicitly
call constructors and destructors, which would eliminate their safety.