C++ Newsletter/Tutorial Issue 3
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:
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: (800) 722-1613 or (970) 490-2462
Fax: (970) 490-2463
FTP: rmii.com /pub2/glenm/newslett (for back issues)
Web: http://www.rmii.com/~glenm