C++Builder influences programming on many levels, and this chapter helps you
to see how. This chapter assumes your basic understanding of the C++ language
and the C++Builder IDE as described in previous chapters.

This chapter begins with the programming practices that form the basis of C++
Builder programming. Some of these differ from those used in pure C++
programming, but they add to your productivity by leveraging the key advantages
of the C++Builder environment.

This is followed by an overview of the VCL (Visual Component Library) that
provides the rapid application development capability unique to C++Builder among
C++ development environments.

Next comes a quick trip through the most useful components on the component
palette.

One of the core advantages of C++Builder is the visual development
environment that you can use to build user interfaces. You will get a trip
through all the important features, including screen layout techniques to create
interfaces that work well as the user changes the window's size, and how to
use Actions to simplify user interface implementation.

Then, a brief discussion of the use of nonvisual components in C++Builder
leads to an in-depth discussion of multithreading to wrap up this trip through
key C++Builder programming practices.

Better Programming Practices in C++Builder

This section looks at some ways to improve how you write C++ code in
C++Builder. Entire books are devoted to better C++ programming, and you are
encouraged to read such texts to deepen your understanding of C++. The topics
discussed here are those that have particular relevance to C++Builder, and those
that are often misunderstood or misused by those new to C++Builder.

Use a String Class Instead of char*

Say goodbye to char* for string manipulation. Use either the string
class provided by the C++ Standard Library or the VCL's native string class
AnsiString (which has been conveniently typedefed to
String). You can even use both. Access the C++ Standard Library's
string class by including the statement

#include <string>

at the top of your code. If portability across operating systems is a goal,
you cannot use the VCL, so this is the string class to use. Otherwise, use
AnsiString, which has the advantage that it is the string
representation used throughout the VCL and, thus, allows your code to work
seamlessly with the VCL. For circumstances in which an old-style char*
string is required, such as to pass a parameter to a Win32 API call, both string
classes offer the c_str() member function, which returns such a string.
In addition, the AnsiString class also offers the popular old-style
sprintf() and printf() functions (for concatenating strings)
as member functions. It offers two varieties of each: a standard version and a
cat_ version. The versions differ in that the cat_ version
adds the concatenated string to the existing AnsiString, and the
standard version replaces any existing contents of the AnsiString. The
difference between the sprintf() and printf() member functions
is that sprintf() returns a reference to the AnsiString, and
printf() returns the length of the final formatted string (or the
length of the appended string, in the case of cat_printf). The function
declarations are

These member functions ultimately call vprintf() and
cat_vprintf() in their implementation. These member functions take a
va_list as their second parameter as opposed to a variable argument
list. This requires the addition of the #include <stdarg.h>
statement in your code. The function declarations are

The respective printf() and sprintf() functions perform the
same task, differing only in their return types. As a result, this is the only
criterion that is required when deciding which of the two to use.

CAUTION

Note that the printf() and sprintf()AnsiString
member functions in C++Builder version 4 are the same as the
cat_printf() and cat_sprintf() functions in version 5,
not the printf() and sprintf()AnsiString
member functions. Care should be taken when converting code between those two
versions.

Understand References and Use Them Where Appropriate

References are often misunderstood and, therefore, are not used as often as
they should be. Often, it is possible to replace pointers with references,
making the code more intuitive and easier to maintain. This section looks at
some of the features of references and when they are most appropriately
used.

The reason for the abundance of pointer parameters in the VCL in C++Builder
is often a key point of controversy among C++ programmers moving to
C++Builder.

A reference always refers to only one object, its referent, and it
cannot be rebound to refer to a different object (object in this context
includes all types). A reference must be initialized on creation; a reference
cannot refer to nothing (NULL). Pointers, on the other hand, can point
to nothing (NULL), can be re-bound, and do not require initialization
on creation. A reference should be considered an alternative name for an object,
whereas a pointer should be considered an object in itself. Anything that is
done to a reference is also done to its referent and vice versa. This is because
a reference is just an alternative name for the referent; they are the same. You
can see, therefore, that references, unlike pointers, are implicitly
dereferenced.

If you change the value of Y or X, you also change the
value of X or Y, respectively, because X and
Y are two names for the same thing. Another example of declaring a
reference to a dynamically allocated variable is

TBook* Book1 = new TBook(); // Declare and create a TBook object
TBook& Book2 = *Book1; // Declare a TBook reference,
// i.e. Book2, and initialize it
// to refer to the object pointed
// by Book1
The object pointed to by Book1 is the referent of the reference Book2.

One of the most important uses for references is the passing of user-defined
types as parameters to functions. A parameter to a function can be passed by
reference by making the parameter a reference and calling the function as if it
were passed by value. For example, the following function is the typical swap
function for two ints:

Number1 and Number2 are passed by reference to
swap, and, therefore, X and Y become alternative
names for Number1 and Number2, respectively, within the
function. What happens to X also happens to Number1 and what
happens to Y also happens to Number2. A predefined type such
as an int should be passed by reference only when the purpose is to
change its value; otherwise, it is generally more efficient to pass by value.
The same cannot be said for user-defined types (classes, structs, and
so on). Rather than pass such types to functions by value, it is more efficient
to pass such types by const reference or, if the type is to be changed,
by non-const reference or pointer. However,

void DisplayMessage(const AnsiString& message)
{
//Display message.
// message is an alias for the AnsiString argument passed
// to the function. No copy is made and the const qualifier
// states that the function will not (cannot) modify message
}

This is because AnsiString itself implements a copy-on-write, shared
string model, and thus only a very small object is passed from caller to
function.

But for non-VCL classes (because VCL classes other than AnsiString
are usually passed as const pointers) the first format is better for
two reasons. First, the parameter is passed by reference. This means that when
the function is called, the object used as the calling argument is used
directly, rather than being copied. The copy constructor of AnsiString
does not need to be invoked (as it would be on entering the second function),
and neither does the destructor, as it would be at the end of the second
function when message goes out of scope. Second, the const
keyword is used in the first function to signify that the function will not
modify message through message. Both functions are called in
the same way:

AnsiString Message = "Hello!";
DisplayMessage(Message);

Functions can also return references, which has the side effect of the
function becoming an lvalue (a value that can appear on the left side of
an expression) for the referent. This also enables operators to be written that
appear on the left side of an expression, such as the subscript operator. For
example, given the Book class, an ArrayOfBooks class can be
defined as follows:

This is possible because the value returned by the operator is the actual
referent, not a copy of the referent.

Generally, you can say that references are preferred to pointers because they
are safer (they can't be re-bound and don't require testing for
NULL because they must refer to something). Also, they don't
require explicit dereferencing, making code more intuitive.

But what about the pointers used in C++Builder's VCL?

The extensive use of pointers in the VCL is caused by the fact that the VCL
is written in, and must remain compatible with Delphi (Delphi), which uses
Delphi-style references. A Delphi-style reference is closer to a C++ pointer
than a C++ reference. This has the side effect that, when the VCL is used with
C++, pointers have to be used as replacements for Delphi references. This is
because a Delphi reference (unlike a C++ reference) can be set to
NULL and can be re-bound. In some cases it is possible to use
reference parameters instead of pointer parameters, but because all VCL-based
objects are dynamically allocated on free store and, therefore, are referred to
through pointers, the pointers must be dereferenced first. Because the VCL
relies on some of the features of Delphi references, pointers are used for
object parameter passing and returning. Remember that a pointer parameter is
passed by value, so the passed pointer will not be affected by the function. You
can prevent modification of the object pointed to by using the const
modifier.

Avoid Using Global Variables

Unless it is absolutely necessary, don't use global variables in your
code. Apart from polluting the global namespace (and increasing the chance of a
name collision), it increases the dependencies between translation units that
use the variables. This makes code difficult to maintain and minimizes the ease
with which translation units can be used in other programs. The fact that
variables are declared elsewhere also makes code difficult to understand.

One of the first things any astute C++Builder programmer will notice is the
global form pointers present in every form unit. This might give the impression
that using global variables is OK; after all, C++Builder does it. However,
C++Builder does this for a reason, which we will discuss at the end of this
section. For now, we will examine some of the alternatives to declaring global
variables.

Let's assume that global variables are a must. How can we use global
variables without incurring some of the side effects that they produce? The
answer is that we use something that acts like a global variable, but is
not one. We use a class with a member function that returns a value of, or
reference to (whichever is appropriate), a static variable that
represents our global variable. Depending on the purpose of our global variables
(for example, global to a program or global to a library), we may or may not
need access to the variables through static member functions. In other words, it
might be possible to instantiate an object of the class that contains the
static variables when they are required. We consider first the case
where we do require access to the static variables (representing our
global variables) through static member functions. We commonly refer to this
kind of class as a module.

With a module of global variables, you improve your representation of the
variables by placing them into a class, making them private static
variables, and using static getters and setters to access them (for
more information, see Large-Scale C++ Software Design by Lakos, 1996, p.
69). This prevents pollution of the global namespace and gives a certain degree
of control over how the global variables are accessed. Typically, the class
would be named Global. Hence, two global variables declared as

Accessing Number is now done through Global::getNumber()
and Global::setNumber(). Average is accessed similarly. The
class Global is effectively a module that can be accessed throughout
the program and does not need to be instantiated (because the member data and
functions are static).

Often such an implementation is not required, and it is possible to create a
class with a global point of access that is constructed only when first
accessed. This has the benefit of allowing control over the order of
initialization of the variables (objects must be constructed before first use).
The method used is to place the required variables inside a class that cannot be
directly instantiated, but accessed only through a static member function that
returns a reference to the class. This ensures that the class containing the
variables is constructed on first use and is constructed only once.

This approach is often referred to as the Singleton pattern (for more
information, see Design Patterns: Elements of Reusable Object-Oriented
Software by Gamma et al., 1995, p. 127). Patterns are a way of
representing recurring problems and their solutions in object-based programs.
For more on patterns, see Chapter 4, " Creating Custom
Components."

The basic code required to create a Singleton (as such a class is commonly
referred to) is as follows:

The initial call to Instance will create a new Singleton and return
a reference to it. Subsequent calls will simply return a reference. However, the
destructor of the Singleton will not be called; the object is simply abandoned
on free store. If there is important processing that must be executed in the
destructor, the following implementation will ensure that the Singleton is
destructed:

This implementation causes its own problem. It is possible for another static
object to access the Singleton after it has been destroyed. One solution to this
problem is the nifty counter technique (for more information, see C++
FAQs Second Edition, Cline et al., 1999, p. 235, and Large-Scale
C++ Software Design, Lakos, 1996, p. 537), in which a static
counter is used to control when each object is created and destroyed. If you
find the need for this technique, perhaps a rethink of the code would also be
helpful. It might be that a slight redesign could remove the dependency.

It should now be clear that static variables are like global variables and
can almost always be used in place of global variables. Remember, though, that
ultimately global variables should be avoided.

Understand How C++Builder Uses Global Variables

What then of the global form pointer variables in C++Builder? Essentially,
global form pointer variables are present to enable the use of nonmodal forms.
Nonmodal forms are conventional windows that last for long periods of time and
enable you to work with the rest of the application, even while they are open.
Modal forms are dialogs, which block interaction with the rest of the
application.

Nonmodal forms require a global point of access for as long as the form
exists, and it is convenient for the IDE to automatically create one when the
form is made. The default operation of the IDE is to add newly created forms to
the autocreate list, which adds the line

Application->CreateForm(__classid(TFormX), &FormX);

(where X is a number) to the WinMain function in the
project .cpp file. Modal forms do not require this because the
ShowModal() method returns after the forms are closed, making it
possible to delete them in the same scope as they were created. General
guidelines on the use of forms can, therefore, be given.

TIP

You can uncheck the Auto Create Forms option on the forms property page in
the Tools, Environment Options menu to change the behavior of the IDE so that
forms are not automatically added to the autocreate list. When this is done,
forms are instead added to the available list.

First, determine if a form is to be a modal form or a nonmodal form.

If the form is modal, it is possible to create and destroy the form in the
same scope. This being the case, the global form pointer variable is not
required, and the form should not be autocreated. Remove the
Application->CreateForm entry from WinMain either by
deleting it or by removing it from the autocreate list on the forms page in the
Project, Options menu. Next, either delete or comment out the form pointer
variable from the .h and .cpp files, and state explicitly in
the header file that the form is modal and should be used only with the
ShowModal() method. That is, in the .cpp file remove

TFormX* FormX;

and from the .h file, remove

extern PACKAGE TFormX* FormX;

Add a comment such as the following:

// This form is MODAL and should only called with the ShowModal() method.

The use of a try/__finally block ensures that the code is
exception-safe. An alternative to these examples is to use the Standard
Library's auto_ptr class template:

auto_ptr<TFormX> FormX(new TFormX(0));
FormX->ShowModal();

NOTE

You might need to reference the std namespace to create an auto_ptr;
this is done either by using a namespace std or by prefixing auto_ptr
with std::.

Whichever technique you use, you are guaranteed that if the code terminates
prematurely because an exception is thrown, FormX will be automatically
destroyed. With the first technique this happens in the __finally
block; with the second it occurs when auto_ptr goes out of scope. The
second technique can be further enhanced by making the auto_ptrconst because generally it is not required that the auto_ptr
lose ownership of the pointer, as in the following code. (For more information,
see Exceptional C++: 47 Engineering Puzzles, Programming Problems, and
Solutions by Sutter, 2000, p. 158.)

const auto_ptr<TFormX> FormX(new TFormX(0));
FormX->ShowModal();

Of particular note in the code snippets is that 0 (NULL) is passed
as the argument to the AOwner parameter of FormX. This is
because we handle the destruction of the form ourselves.

TIP

Using auto_ptr is an effective way of managing the memory of
VCL-based objects. It is exception-safe and easy to use. For a VCL object that
takes an owner parameter in its constructor, you can simply pass 0
because you know that the object will be deleted when the auto_ptr goes
out of scope.

If the form is non-modal, you must only decide whether you want it
autocreated. If you don't, you must ensure that it is removed from
WinMain. When you want it created later, you can use the form's
global pointer and the new operator. Show the form using the
Show() method. Remember that you cannot delete modal forms because
Show() returns when the form is shown, not when it is closed.
Therefore, it might still be in use. For example, if the form is autocreated,
write

FormX->Show();

Otherwise, create and show it this way:

FormX = new TFormX(this);
FormX->Show();

As an aside to this topic, the practice of declaring variables or functions
as static so that they have scope only within the translation unit in which they
are declared is deprecated. Instead, such variables and functions should be
placed in an unnamed namespace. (For more information, see ANSI/ISO
C++ Professional Programmer's Handbook: The Complete Language by Kalev,
1999, p. 157.)

Understand and Use const in Your Code

The const keyword should be used as a matter of course, not as an
optional extra. Declaring a variable const enables attempted changes to
the variable to be detected at compile time (resulting in an error) and also
indicates the programmer's intention not to modify the given variable.
Moreover, not using the const keyword indicates the programmer's
intention to modify a given variable. The const keyword can be used in
a variety of ways.

First, it can be used to declare a variable as a constant:

const double PI = 3.141592654;

This is the C++ way to declare constant variables. Do not use
#define statements. Note that const variables must be
initialized. The following shows the possible permutations for declaring
const variables. Pointer and reference declarations are read from right
to left, as the following examples show:

After reviewing the previous examples, it is helpful to reiterate how
const is used with pointer declarations. As stated previously, a
pointer declaration is read from right to left so that in int * const
the const refers to the *. Hence, the pointer is constant, but
the int it points to can be changed. With int const * the
const refers to the int. In this case, the int itself
is constant, though the pointer to it is not. Finally, with int const *
const, both the int and the * are constant. Also remember
that int const and const int are the same, so const int *
const is the same as int cosnt * const.

If you want to declare a literal string of chars, declare it as one
of the following:

Function parameters should be declared as const in this fashion when
it is appropriate, such as when the intention of the function is not to modify
the argument that is passed to the function. For example, the following function
states that it will not modify the arguments passed to it:

Another way of thinking about this is to assume that if the const
keyword is not used for a parameter, it must be the intention of the function to
modify that parameter's argument, unless the parameter is pass-by-value (a
copy of the parameter is used, not the parameter itself). Notice that declaring
int LengthOfArray as a const is inappropriate because this is
pass-by-value. LengthOfArray is a copy, and declaring it as a
const has no effect on the argument passed to the function. Similarly,
ArrayOfDouble is declared as follows:

const double* ArrayOfDouble

not

const double* const ArrayOfDouble

Because the pointer itself is a copy, only the data that it points to needs
to be made const.

The return type of a function can also be const. Generally, it is
not appropriate to declare types returned by value as const, except in
the case of requiring the call of a const-overloaded member function.
Reference and pointer return types are suitable for returning as
consts.

Member functions can be declared const. A const member
function is one that does not modify the this object (*this).
Hence, it can call other member functions inside its function body only if they
are also const. To declare a member function const, place the
const keyword at the end of the function declaration and in the
function definition at the same place. Generally, all getter member functions
should be const because they do not modify *this. For
example

The final area in which const is commonly encountered is when
operators are overloaded by a class and access to both const and
non-const variables is required. For example, if a class
ArrayOfBooks is created to contain Book objects, it is
sensible to assume that the [] operator will be overloaded (so that the
class acts like an array). However, the question of whether the []
operator will be used with const or non-const objects must be
considered. The solution is to const-overload the operator, as the
following code indicates:

The ArrayOfBooks class can use the [] operator on both
const and non-constBooks. For example, if an
ArrayOfBooks object is passed to a function by reference to
const, it would be illegal for the array to be assigned to using the
[] operator. This is because the value indexed by i would be a
const reference, and the const state of the passed array would
be preserved.

Remember, know what const is and use it whenever you can.

Be Familiar with the Principles of Exceptions

Exceptions offer a mechanism for handling runtime errors in a program.
Several approaches can be taken to handling runtime errors, such as returning
error codes, setting global error flags, and exiting the program. But, in most
circumstances, an exception is the only appropriate method. (For more
information, see ANSI/ISO C++ Professional Programmer's Handbook: The
Complete Language by Kalev, 1999, p. 113.)

Exceptions will commonly be encountered in two forms in C++Builder programs:
C++ exceptions and VCL exceptions. Generally, the principles involved with both
are the same, but there are some differences.

C++ uses three keywords to support exceptions: try, catch,
and throw. C++Builder extends its exception support to include the
__finally keyword.

The try, catch, and __finally keywords are used as
headers to blocks of code (that is, code that is enclosed between braces). Also,
for every try block there must always be one or more
catch blocks or a single __finally block.

The try Keyword

The try keyword is used in one of two possible ways. The first and
simplest is as a simple block header, to create a try block within a
function. The second is as a function block header, to create a functiontry block, either by placing the try keyword in front of the
function's first opening brace or, in the case of constructors, in front of
the colon that signifies the start of the initializer list.

NOTE

C++Builder does not currently support function try blocks. However,
because it makes a real difference only with constructors, and even then has
little impact on their use, it is unlikely that its omission will have any
effect. For those who are interested, it will be supported in version 6 of the
compiler.

The catch Keyword

Normally, at least one catch block will immediately follow any
try block (or function try block). A catch block will
always appear as the catch keyword followed by parentheses containing a
single exception type specification with an optional variable name. Such a
catch block (commonly referred to as an exceptionhandler) can catch only an exception whose type exactly matches
the exception type specified by the catch block. However, a
catch block can be specified to catch all exceptions by using
the catch all ellipses exception type specifier, catch(...).

The __finally Keyword

The last of these, __finally, has been added to allow the
possibility of performing cleanup operations or ensuring certain code is
executed regardless of whether an exception is thrown. This works because code
placed inside a __finally block will always execute, even when an
exception is thrown in the corresponding try block. This allows code to
be written that is exception-safe and will work properly in the presence of
exceptions. A typical try/__finally scenario is

try
{
// Code that may throw an exception
}
__finally
{
// Code here is always executed, even if
// an exception is thrown in the preceding
// try block
}

It should be noted that try/catch and
try/__finally constructs can be nested inside other
try/catch and try/__finally constructs.

The throw Keyword

The throw keyword is used in one of two ways. The first is to throw
(or rethrow) an exception, and the second is to allow the specification of the
type of exceptions that a function might throw. In the first case (to throw or
rethrow an exception), the throw keyword is followed optionally by
parentheses containing a single exception variable (often an object) or simply
the single exception variable after a space, similar to a return
statement. When no such exception variable is used, the throw keyword
stands on its own. Then, its behavior depends on its placement. When placed
inside a catch block, the throw statement rethrows the
exception currently being handled. When placed elsewhere, such as when there is
no exception to rethrow, it causes terminate() to be called, ultimately
ending the program. It is not possible to use throw to rethrow an
exception in VCL code. The second use of the throw keyword is to allow
the specification of the exceptions that a function might throw. The syntax for
the keyword is

throw(<exception_type_list>)

The exception_type_list is optional and, when excluded,
indicates that the function will not throw any exceptions. When included, it
takes the form of one or more exception types separated by commas. The exception
types listed are the only exceptions the function can throw.

Unhandled and Unexpected Exceptions

In addition to the three keywords described, C++ offers mechanisms to deal
with thrown exceptions that are not handled by the program and exceptions that
are thrown, but are not expected. This might include an exception that is thrown
inside a function with an incompatible exception specification.

When an exception is thrown, but not handled, terminate() is called.
This calls the default terminate handler function, which by default calls
abort(). This default behavior should be avoided because
abort() does not ensure that local object destructors are called. To
prevent terminate() being called as a result of an uncaught exception,
the entire program can be wrapped inside a try/catch(...)
block in WinMain() (or main() for command-line programs). This
ensures that any exception will eventually be caught. If terminate() is
called, you can modify its default behavior by specifying your own terminate
handler function. Simply pass the name of your terminate handler function as an
argument to the std::set_terminate() function. The
<stdexcept> header file must be included. For example, given a
function declared as

void TerminateHandler();

The code required to ensure that this handler is called in place of the basic
terminate() handler is

#include <stdexcept>
std::set_terminate(TerminateHandler);

When an exception is thrown that is not expected, unexpected() is
called. Its default behavior is to call terminate(). Again, the
opportunity exists to define your own function to handle this occurrence. To do
so, call std::set_unexpected(), passing the function handler name as an
argument. The <stdexcept> header file must be included.

Using Exceptions

This brings the discussion to consideration of the exceptions that can and
should be thrown by a function and where such exceptions should be caught. This
should be decided when you are designing your code, not after it has already
been written. To this end, you must consider several things when you write a
piece of code. Some of the topics are very complex, and it is beyond the scope
of this book to cover all the issues involved.

You must consider if the code you have written could throw one or more
exceptions. If so, you must then consider if it is appropriate to catch one or
more of the exceptions in the current scope or let one or more of them propagate
to an exception handler outside the current scope. If you do not want one or
more of the exceptions to propagate outside the current scope, you must place
the code in a try block and follow it with the one or more appropriate
catch blocks to catch any desired exceptions (or all exceptions, using
a catch-all block). To this end, you should be aware of the exceptions built
into the language itself, the C++ Standard Library, and the VCL, and be aware of
when they can be thrown. For example, if new fails to allocate enough
memory, std::bad_alloc is thrown.

Throw an exception in a function only when it is appropriate to do so, when
the function cannot meet its promise. (For more information, see C++
FAQs, Second Edition, Cline et al., 1999, p. 137.)

You should catch an exception only when you know what to do with it, and you
should always catch an exception by reference. (For more information, see
More Effective C++: 35 New Ways to Improve Your Programs and Designs by
Meyers, 1996, p. 68.) VCL exceptions cannot be caught by value. Also, it
might not be possible to fully recover from an exception, in which case, the
handler should perform any possible cleanup, and then rethrow the exception.

You should understand when and how to use exception specifications for
functions and be wary of the possibility of writing an incorrect specification.
This will result in unexpected() being called if an unspecified
exception is thrown inside a function and it is not handled within that
function.

You should ensure that you write exception-safe code that works properly in
the presence of exceptions. For example, simple code such as this is not
exception safe:

If an exception is thrown between the creation and deletion of the form, it
will never be deleted, so the code does not work properly in the presence of
exceptions. For an exception-safe alternative, see the section "Avoid Using
Global Variables," earlier in this chapter.

If you are writing library or container classes, endeavor to write code that
is exception-neutralcode that propagates all exceptions to the
caller of the function that contains the code. (For more information, see
Exceptional C++: 47 Engineering Puzzles, Programming Problems, and
Solutions by Sutter, 2000, p. 25.)

Never throw an exception from a destructor because the destructor might have
been called as a result of stack unwinding after a previous exception was
called. This calls terminate(). Destructors should have an exception
specification of throw().

A Final Note on Exceptions

Finally, you should appreciate the differences between VCL and C++
exceptions. VCL exceptions allow operating system exceptions to be handled as
well as exceptions generated from within the program. Such exceptions must be
caught by reference. VCL exceptions generated from within the program cannot be
caught by value. An advantage of VCL exceptions is that they can be thrown and
caught within the IDE.

Use new and delete to Manage Memory

The VCL requires that all classes that inherit from TObject be
created dynamically from free store. Free store is often referred to as the
heap, but free store is the correct term when applied to memory allocated and
deallocated by new and delete. The term heap should be
reserved for the memory allocated and deallocated by malloc() and
free(). (For more information, see Exceptional C++: 47 Engineering
Puzzles, Programming Problems, and Solutions by Sutter, 2000, p. 142.) This
means a lot of calls to new and delete in C++Builder programs,
so it is important to understand a few things about how new and
delete work.

WARNING

A Non-Plain Old Data (Non-POD) object is essentially all but the most trivial
of classes. Such objects must have their memory allocated by using new;
the C equivalent malloc() will not suffice (its behavior is undefined)
and be subsequently deallocated with delete, not free(). The
new and delete operators ensure that, in addition to the
allocation/deallocation of memory, the object's constructor and destructor,
respectively, are called.

The new operator also returns a pointer that is suitable to the
object created; not merely a void pointer that must be cast to the
required type. new and delete call operator
new/operator delete, respectively, to allocate/deallocate memory,
and these can be overloaded for specific classes. This enables the customization
of memory allocation/deallocation behavior. This is not possible with
malloc() and free().

A successful call to new allocates sufficient memory in free store
(using operator new) calls the object's constructor and returns a
pointer of the type pointer-to-the-object-type-created. A correctly initialized
object is the result. Subsequently, calling delete calls the
object's destructor and deallocates the memory obtained previously by
calling new.

WARNING

Never call a VCL object's Free() method to destroy a VCL
object. Always use delete. This ensures that the object's
destructor is called and that the memory allocated previously with new
is freed. Free() does not guarantee this, and it is bad practice to use
it.

If the call to new is unsuccessful, a std::bad_alloc
exception is thrown. Note that the bad_alloc exception is defined in
the standard library file <new>. Hence, you must include
#include <new> in your program, and it is in the std
namespace. It does not return NULL. Therefore, you should not check the
return pointer for equality with NULL. The program should be prepared
to catch the std::bad_alloc exception and, if the function that calls
new does not catch the exception, it should pass the exception outside
the function so that calling code has the opportunity to catch it. Either of the
following would be appropriate:

The use of exceptions enables the code that handles the error to be
centralized, which leads to safer code that is more intuitive. The
throw keyword added to the function header is called an exception
specification. The effect of its inclusion in the function header is to
specify which exceptions the function can throw. For more explanation refer to
the section "Be Familiar with the Principles of Exceptions," earlier
in this chapter. In the case of the first CreateObject() function, a
throw() exception specifier is used to indicate that no exception will
be thrown by the function. This is acceptable because the only exception that
can be thrown, std::bad_alloc, is caught and dealt with by the function
itself. In the case of the second implementation of CreateObject(), the
exception specifier throw(std::bad_alloc) is used to indicate that the
only exception that the function can throw is std::bad_alloc. This
should be caught and handled by one of the calling routines.

There is also the possibility of writing your own out-of-memory function
handler to deal with failed memory allocation. To set a function as a handler
for out-of-memory conditions when using new, call the
set_new_handler() function (also defined in <new>),
passing as a parameter the name of the function you will use as the
out-of-memory handler. For example, if you write a function (nonmember or
static member) called OutOfMemory to handle such occurrences,
the necessary code is

#include <new>
void OutOfMemory()
{
// Try to free some memory
// if there is now enough memory then this
// function will NOT be called next time
// else either install a new handler or throw an exception
}
// Somewhere in the main code, near the start write:
std::set_new_handler(OutOfMemory);

This code requires some explanation, because the sequence of events that
occurs when new fails dictates how the OutOfMemory function
should be written. If new fails to allocate enough memory,
OutOfMemory is called. OutOfMemory tries to free some memory
(how this is done will be discussed later); new will then try again to
allocate the required memory. If it is successful, you are finished. If it is
unsuccessful, the process just described will be repeated. In fact, it will
repeat infinitely until either enough memory is allocated or the
OutOfMemory function terminates the process.

To terminate the process, the OutOfMemory function can do several
things. It can throw an exception (such as std::bad_alloc()), it can
install a different memory handler that can then try to make more memory
available, it can assign NULL to set_new_handler
(std::set_new_handler(0)), or it can exit the program (not
recommended). If a new handler is installed, then this series of events will
occur for the new handler (which is called on the subsequent failed attempt). If
the handler is set to NULL (0), then no handler will be called, and the
exception std::bad_alloc() will be thrown.

Making more memory available is dependent on the design of the program and
where the memory shortage arises. If the program keeps a lot of memory tied up
for performance reasons but does not always require it to be available at all
times, then such memory can be freed if a shortage occurs. Identifying such
memory is the difficult part. If there is no such memory usage in the program,
the shortage will be a result of factors external to the program such as other
memory-intensive software or physical limitations. There is nothing that can be
done about physical limitations, but it is possible to warn the user of a memory
shortage so that memory-intensive software can be shut down, thereby freeing
additional memory.

The trick is to give an advance warning before all the memory is used. One
approach is to preallocate a quantity of memory at the beginning of the program.
If new fails to allocate enough memory, the memory successfully
allocated can be freed. The user is warned that memory is low and told to try to
free more memory for the application. Assuming that the preallocated block was
large enough, the program should be able to continue operating as normal if the
user has freed additional memory. This preemptive approach is simple to
implement and reasonably effective.

It is important to note that if you want to allocate raw memory only,
operator new and operator delete should be used instead of the
new and delete operators. (For more information, see More
Effective C++: 35 New Ways to Improve Your Programs and Designs by Meyers,
1996, p. 38.) This is useful for situations in which, for example, a structure
needs to be allocated dynamically, and the size of the structure is determined
through a function call before the dynamic allocation. This is a common
occurrence in Win32 API programming:

It is clear that the use of malloc() and free() should not
be required.

Finally, we will discuss the use of new and delete in
dynamically allocating and deallocating arrays. Arrays are allocated and
deallocated using operator new[] and operator delete[],
respectively. They are separate operators from operator new and
operator delete. When new is used to create an array of
objects, it first allocates the memory for the objects (using operator
new[]), and then calls its default constructor to initialize each object.
Deleting an array with delete performs the opposite task: It calls the
destructor for each object, and then deallocates the memory (using operator
delete[]) for the array. So delete knows to call operator
delete[] instead of operator delete, a [] is placed
between the delete keyword and the pointer to the array to be
deleted:

delete [] SomeArray;

Allocating a single-dimensional array is straightforward. The following
format is used:

TBook* ArrayOfBooks = new TBook[NumberOfBooks];

Deleting such an array is also straightforward. However, remember that the
correct form of delete must be useddelete []. For
example

delete [] ArrayOfBooks;

Remember that [] tells the compiler that the pointer is to an array,
as opposed to simply a pointer to a single element of a given type. If an array
is to be deleted, it is essential that delete [] be used, not
delete. If delete is used erroneously, at best only the first
element of the array will be deleted. You know that when an array of objects is
created, the default constructor is used. This means that you will want to
ensure that you have defined the default constructor to suit your needs.
Remember that a compiler-generated default constructor does not initialize the
classes's data members. Also, you will probably want to overload the
assignment operator (=) so that you can safely assign object values to
the array objects. A two-dimensional array can be created using code such as the
following:

One thing remains unsaid: If you want to have an array of objects, a better
approach is to create a vector of objects using the vector template
from the STL. It enables any constructor to be used and also handles memory
allocation and deallocation automatically. It will also reallocate memory if
there is a memory shortage. This means that the use of the C library function
realloc() is no longer required.

Placement new (allocation at a predetermined memory location) and
nothrow new (does not throw an exception on failure, it returns
NULL instead) have not been discussed because they are beyond the scope
of this section.

Understand and Use C++-Style Casts

The four C++ casts are outlined in Table 3.1.

Table 3.1 C++-Style Casts

Cast

General Purpose

static_cast<T>(exp)

Used to perform casts such as an int to a double.

T and exp can be pointers, references, arithmetic
types (such as int), or enum types. You cannot cast from one
type to another; for example, from a pointer to an arithmetic.

dynamic_cast<T>(exp)

Used to perform casting down or across an inheritance hierarchy. For example,
if class X inherits from class O, a pointer to class O can be cast to a pointer
to class X, provided the conversion is valid.

T can be a pointer or a reference to a defined class type or
void*.

exp can be a pointer or a reference. For a conversion from a
base class to a derived class to be possible, the base class must contain at
least one virtual function; in other words, it must be polymorphic.

One important feature of dynamic_cast is that if a conversion
between pointers is not possible, a NULL pointer is returned; if a
conversion between references is not possible, a std::bad_cast
exception is thrown (include the header file <typeinfo>). As a
result, the conversion can be checked for success.

const_cast<T>(exp)

This is the only cast that can affect the const or volatile
nature of an expression. It can be either cast off or cast on. This is the only
thing const_cast is used for.

For example, if you want to pass a pointer to const data to a
function that only takes a pointer to non-const data, and you know the
data will not be modified, you could pass the pointer by const_casting
it.

T and exp must be of the same type except for their
const or volatile factors.

reinterpret_cast<T>(exp)

Used to perform unsafe or implementation-dependent casts. This cast should be
used only when nothing else will do. This is because it enables you to
reinterpret the expression as a completely different type, such as to cast a
float* to an int*. It is commonly used to cast between
function pointers. If you find yourself needing to use
reinterpret_cast, decide carefully if the approach you are taking is
the right one, and remember to document clearly your intention (and possibly
your reasons for this approach).

T must be a pointer, a reference, an arithmetic type, a pointer to a
function, or a pointer to a member function. A pointer can be cast to an
integral type and vice versa.

The casts most likely to be of use are static_cast
(for trivial type conversions such as int to double) and
dynamic_cast.

An example of using static_cast can be found in the last line of the
following code:

One of the times when dynamic_cast is commonly used in C++Builder is
to dynamic_castTObject* Sender or TComponent* Owner,
to ensure that Sender or Owner is of a desired class, such as
TForm. For example, if a component is placed on a form, it can be
necessary to distinguish if it was placed directly or was perhaps placed on a
Panel component. To carry out such a test, the following code is
required:

First a pointer of the required type is declared, and then it is set equal to
the result of the dynamic_cast. If the cast is unsuccessful, the
pointer will point to the required type and can be used for accessing that type.
If it fails, it will point to NULL and, hence, can be used to evaluate
a Boolean expression. Sender can be similarly used. The situations that
require such casting are many and varied. What is important is to understand
what it is that you want to achieve and make your intention and reasoning
clear.

Each of the C++ casts performs a specific task and should be restricted for
use only where appropriate. The C++ casts are also easily seen in code, making
it more readable.

Know When to Use the Preprocessor

It is not appropriate to use the preprocessor for defining constants or for
creating function macros. Instead, you should use const variables or
enum types for constants and use an inline function (or
inline template function) to replace a function macro. Consider also
that a function macro might not be appropriate anyway (in which case the
inline equivalent would not be required).

For example, the constant ? can be defined as

const double PI = 3.141592654;

If you wanted to place this inside a class definition, you would write

Note that the class constant is made static so that only one copy of
the constant exists for the class. Also notice that the constant is initialized
in the implementation file (typically after the include directive for
the header file that contains the class definition). The exception to this is
the initialization of integral types, char, short,
long, unsigned, and int. These can be initialized
directly in the class definition. When a group of related constants is required,
an enum is a sensible choice:

enum LanguagesSupported { English, Chinese, Japanese, French };

Sometimes an enum is used to declare an integer constant on its
own:

enum { LENGTH = 255 };

Such declarations are sometimes seen inside class definitions. A static
const variable declaration (like that for PI) is a more correct
approach.

Replacing a function macro is also easily achieved. Given the macro

#define cubeX(x) ( (x)*(x)*(x) )

the following inline function equivalent can be written:

inline double cubeX(double x) { return x*x*x; }

Notice that this function takes a double as an argument. If an
int were passed as a parameter, it would have to be cast to a
double. Because you want the behavior of the function to be similar to
that of the macro, you should avoid this necessity. This can be achieved in one
of two ways: Either overload the function or make it a function template. In
this case, overloading the function is the better of the two choices because a
function template would imply that the function could be used for classes as
well, which would most likely be inappropriate. Therefore, an int
version of the inline function could be written as

inline int cubeX(int x) { return x*x*x; }

Generally, you want to avoid using #define for constants and
function macros. #define should be used when writing include guards.
Remember that include guards are written in the header file to ensure that a
header already included is not included again. For example, a typical header
file in C++Builder will look like this:

#ifndef Unit1H // Is Unit1H not already defined?
#define Unit1H // If not then you reach this line and define it
// Header file code placed here...
#endif // End of if Unit1H not defined

This code ensures that the code between #ifndef and #endif
will be included only once. It is a good idea to follow some convention when
choosing suitable defines for header files. C++Builder uses an uppercase
H after the header filename. If you write your own translation units,
you should follow this convention. Of course, you can use a different naming
convention, such as prepending INCLUDED_ to the header filename, but
you should be consistent throughout a project. Using include guards prevents a
header file from being included more than once, but it must still be processed
to see if it is to be included.

TIP

When you follow the IDE naming convention for include guards (appending an
'H' to the end of the header filename), the IDE treats the
translation unit as a set, and it will appear as such in the Project Manager. If
you do not want your .cpp and .h files to be treated in this
way, do not use IDE-style include guards.

It has been shown that for very large projects (or more generally, projects
with large, dense include graphs), this can have a significant effect on compile
times. (For more information, see Large-Scale C++ Software Design by
Lakos, 1996, p. 82.) Therefore, it is worth wrapping all include statements in
an include guard to prevent the unnecessary inclusion of a file that has been
defined already. For example, if Unit1 from the previous code snippet
also included ModalUnit1, ModalUnit2, and ModalUnit3,
which are dialog forms used by other parts of the program, their include
statements could be wrapped inside an include guard as follows:

#ifndef Unit1H // Is Unit1H not already defined?
#define Unit1H // If not then you reach this line and define it
#ifndef ModalUnit1H // Is ModalUnit1H not already defined?
#include "ModalUnit1.h" // No then include it
#endif // End of if Unit1H not defined
#ifndef ModalUnit2H
#include "ModalUnit2.h"
#endif
#ifndef ModalUnit3H
#include "ModalUnit3.h"
#endif
// Header file code placed here...
#endif // End of if Unit1H not defined

This is not pretty, but it is effective. Remember that you must ensure that
the names you define for include guards must not match any name that appears
elsewhere in your program. The define statement will ensure that it is replaced
with nothing, which could cause havoc. That is why a naming convention must be
agreed on and adhered to.

TIP

Note that the Project Manager in C++Builder 5 was improved to include an
expandable list of header file dependencies for each source file included in a
project. Simply click the node beside the source filename to either expand or
collapse the list. Note that the header file dependency lists are based on the
source file's .obj file, hence the file must be compiled at least
once to use this feature. Also note that the list could be out of date if
changes are made without recompilation.

In C++Builder 6, the relationship between .h and .cpp is
extended to automatically loading both into the Source Code Editor and providing
tabs at the bottom of the editor window, so you can easily switch from header to
implementation and back.

Know when using the preprocessor will benefit the program, and when it
won't. Use it carefully and only when necessary.

Learn About and Use the C++ Standard Library

The C++ Standard Library, including the Standard Template Library (STL), is a
constituent part of ANSI/ISO C++, just as the definition for bool is.
You can save a lot of unnecessary coding by learning to use its features in your
programs. The Standard Library has an advantage over homegrown code in that it
has been thoroughly tested and is fast, and it is the standard, so
portability is a big bonus. Standard Library features are summarized in the
following list:

Exceptions, such as bad_alloc, bad_cast,
bad_typeid, and bad_exception

Utilities, such as min(), max(),
auto_ptr<T>, and numeric_limits<T>

Input and output streams, such as istream and
ostream

Containers, such as vector<T>

Algorithms, such as sort()

Function objects (functors), such as
equal_to<T>()

Iterators

Strings, such as string

Numerics, such as complex<T>

Special containers, such as queue<T> and
stack<T>

Internationalization support

Nearly everything in the Standard Library is a template, and most of the
library consists of the STL, so it is very flexible. For example, the
vector template class can be used to store any kind of data of the same
type. As a result, it is a direct replacement for arrays in C++ and should be
used in preference to arrays whenever possible.

In C++Builder 6, Borland introduced the STLPort open source C++ Standard
Library, which should compile and operate in the same fashion as the old Rogue
Wave implementation. STLPort will run both under Windows and Linux, so it is
compatible with CLX programs.