Issue #003
December, 1995


Contents:

World Wide Web Site
Introduction to Namespaces Part 3 - unnamed namespaces
Standard Template Library
Using C++ as a Better C Part 3 - new/delete
Performance - hidden constructor/destructor costs


WORLD WIDE WEB SITE

A Web site has been established, containing technical C++ information
similar to that found in this newsletter.  You can access it as:

        http://www.rmii.com/~glenm


INTRODUCTION TO NAMESPACES - PART 3

In previous issues we talked about how C++ namespaces can be used to
group names together.  For example:

        namespace A {
                void f1();
                void f2();
        }

        namespace B {
                void f1();
                void f2();
        }

The members of the namespace can be accessed by using qualified names,
for example:

        void g() {A::f1();}

or by saying:

        using namespace A;
        void g() {f1();}

Another interesting aspect of namespaces is that of the unnamed
namespace:

        namespace {
                void f1();
                int x;
        }

This is equivalent to:

        namespace unique_generated_name {
                void f1();
                int x;
        }
        using namespace unique_generated_name;

All unnamed namespaces in a single scope share the same unique name.
All global unnamed namespaces in a translation unit are part of the
same namespace and are different from similar unnamed namespaces in
other translation units.  So, for example:

        namespace {
                int x1;
                namespace {
                        int y1;
                }
        }

        namespace {
                int x2;
                namespace {
                        int y2;
                }
        }

x1 and x2 are in the same namespace, as are y1 and y2.

Why is this feature useful?  It provides an alternative to the keyword
"static" for controlling global visibility.  "static" has several
meanings in C and C++ and can be confusing.  If we have:

        static int x;
        static void f() {}

we can replace these lines with:

        namespace {
                int x;
                void f() {}
        }


THE STANDARD TEMPLATE LIBRARY

STL is a C++ library that has been voted by ANSI to be part of the C++
standard library.

We normally think of software libraries as being sets of functions like
printf() or malloc().  But the STL library is different.  It's a
collection of C++ templates, that is, parameterized types.  For
example, there is a template for manipulating lists, and you can have
lists of ints or doubles or void*** pointers or class A objects.  This
is what is meant by "template" or "parameterized type".  A template is
kind of like a C++ class except that you can specify parameters to the
template to indicate what types it should operate upon.

The virtue of this approach is that one can implement an algorithm
once, say for sorting a list, and then use the algorithm for any types
of data that would be in a list -- numbers, strings, class objects,
and so on.

Templates are a little bit like C macros, and STL is distributed as a
set of header files.  Combining a template with particular argument
types is a process known as "instantiation", and a template bound to
particular arguments is known as a "template class".  So, for example,

        template <class T> class List { ... };

is a List template declaration, and

        List<double> dlist;

declares a variable "dlist" of the template class List<double>, which
is instantiated by supplying the type argument "double" to the List
template.

You can find out more about STL via this Web site:

        http://www.cs.rpi.edu/~musser/stl.html

or download an implementation of the library via FTP from:

        butler.hpl.hp.com


USING C++ AS A BETTER C - PART 3

In the first newsletter we talked about using C++ as a better C.  This
term doesn't have a precise meaning, but one way of looking at it is
to focus on the features C++ adds to C, exclusive of the most obvious
one, namely the class concept used in object-oriented programming.

One of these features is operator new and operator delete.  These are
intended to replace malloc() and free() in the C standard library.  To
give an example of how these are similar and how they differ, suppose
that we want to allocate a 100-long vector of integers for some
purpose.  In C, we would say:

        int* ip;

        ip = (int*)malloc(sizeof(int) * 100);

        ...

        free((void*)ip);

With new/delete in C++, we would have:

        int* ip;

        ip = new int[100];

        ...

        delete ip;

The most obvious difference is that the C++ approach takes care of the
low-level details necessary to determine how many bytes to allocate.
With the C++ new operator, you simply describe the type of the desired
storage, in this example "int[100]".

The C and C++ approaches have several similarities:

        - neither malloc() nor new initialize the space to zeros

        - both malloc() and new return a pointer that is suitably
        aligned for a given machine architecture

        - both free() and delete do nothing with a NULL pointer

malloc() returns NULL if the space cannot be obtained.  Many versions
of new in existing C++ compilers do likewise.  However, the draft ANSI
C++ standard says that a failure to obtain storage should result in an
exception being thrown or should result in the currently installed
new handler being invoked.  In these newsletters we are assuming that
NULL is returned.

The idea of a new handler can be illustrated as follows:

        extern "C" int printf(const char*, ...);
        extern "C" void exit(int);

        typedef void (*new_handler)(void);

        new_handler set_new_handler(new_handler);

        void f()
        {
                printf("new handler invoked due to new failure\n");
                exit(1);
        }

        main()
        {
                float* p;

                set_new_handler(f);

                for (;;)
                        p = new float[5000];    // something that will
                                                // fail eventually
                return 0;
        }

A new handler is a way of establishing a hook from the C++ standard
library to a user program.  set_new_handler() is a library function
that records a pointer to another function that is to be called in the
event of a new failure.

It is possible to define your own new and delete functions.  For
example:

        void* operator new(size_t s)
        {
                // allocate and align storage of size s

                // handle failure via new_handler or exception

                // return pointer to storage
        }

        void operator delete(void* p)
        {
                // handle case where p is NULL

                // handle deallocation of p block in some way
        }

size_t is a typedef, typically defined to mean "unsigned int".  It's
found in a header file that may vary between compiler implementations.


PERFORMANCE TIP

Consider a short example of C++ code:

        class A {
                int x, y, z;
        public:
                A();
        };

        class B {
                A a;
        public:
                B() {}
        };

        A::A() {x = 0; y = 0; z = 0;}

Class A has a constructor A::A(), used to initialize three of the
class's data members.  Class B has a constructor declared inline
(defined in the body of the class declaration).  The constructor is
empty.

Suppose that we use a lot of B class objects in a program.  Each
object must be constructed, but we know that the constructor function
body is empty.  So will there be a performance issue?

The answer is possibly "yes", because the constructor body really is
NOT empty, but contains a call to A::A() to construct the A object
that is part of the B class.  Direct constructor calls are not used in
C++, but conceptually we could think of B's constructor as containing
this code:

        B::B() {a.A::A();}      // construct "a" object in B class

There's nothing sneaky about this way of doing things; it falls
directly out of the language definition.  But in complex cases, such
as ones involving multiple levels of inheritance, a seemingly empty
constructor or destructor can in fact contain a large amount of
processing.

-------------------------

Copyright (c) 1995 Glen McCluskey.  All Rights Reserved.

This newsletter may be further distributed provided that it is copied
in its entirety, including the newsletter number at the top and the
copyright and contact information at the bottom.

Glen McCluskey & Associates
Professional C++ Consulting
Internet: glenm@glenmccl.com
Phone: (970) 490-2462
Fax: (970) 490-2463
FTP: rmii.com /pub2/glenm/newslett (for back issues)
Web: http://www.rmii.com/~glenm
