Previous | Contents | Index |
allocator();
Default allocator constructor.~allocator();
Allocator destructor.void * allocate (size_type n, void * = 0)
Allocate n*size_type bytes of storage.void deallocate (void* p)
Deallocate the storage pointed to by p.size_type max_size (size_type size) const
Returns the largest size for which allocate might succeed.
The allocator_interface class excepts two input parameters, an alternate allocator and the type of the objects to be allocated. The class has the following syntax:
template <class Allocator,class T> class allocator_interface { //... } |
allocator_interface();
Default constructor.allocator_interface(const Allocator& a);
Constructs an allocator_interface and stores the allocator object internally.pointer address (T& x);
Returns the address of the reference x as a pointer.size_type max_size () const;
Returns the largest size for which allocate() of an object of type T might succeed.pointer allocate(size_type n, pointer = 0)
Allocates n*sizeof(T) bytes of storage aligned appropriately of objects of type T. Throws bad_alloc if storage can not be obtained. The second parameter is intended to be used to allocate memory at a particular location.void deallocate(pointer p)
Deallocates the storage that p points to.void construct(pointer p, const T& val)
Constructs an object of type T with an initial value of val at the location specified by p.void destroy(T* p)
Calls the destructor on the object that p points to but does not delete the object.
Allocators can be designed to provide a customized memory management model. To do this, define an allocator that has the same interface as the library allocator.
The following is an example of a customized allocator class that supports the DEC C++ allocator:
template <class T> class shared_allocator { public: typedef size_t size_type; typedef ptrdiff_t difference_type; shared_allocator() {;} ~shared_allocator() {;} void * allocate(size_type n, void* = 0) { return (void*) new size_t[n]; } void deallocate (void *p) { delete(p); } size_type max_size (size_type size) const { return 1 > UINT_MAX/size ? size_type(1) : size_type(UINT_MAX/size); } }; // end shared_allocator<T> class shared_allocator<void> { public: typedef size_t size_type; typedef ptrdiff_t difference_type; typedef void* pointer; typedef const void* const_pointer; typedef void value_type; shared_allocator() {;} ~shared_allocator() {;} void * allocate (size_type n, void * = 0) { return (void *) new size_t[n]; } void deallocate (void* p) { delete(p); } size_type max_size (size_type size) const { return 1 > UINT_MAX/size ? size_type(1) : size_type(UINT_MAX/size); } }; // shared_allocator<void> |
Custom allocators are easily used with existing standard library containers. You need only to provide an allocator type when you instantiate a container as follows:
vector <int, shared_allocator<int> > v1; list <int, shared_allocator<int> > l1; |
To implement custom containers, your container must define the following:
The following is a sample of what a container might look like:
template <class T, class Allocator> class my_container { private: typedef allocator_interface<Allocator,T> value_alloc_type; Allocator my_allocator; } Any member function which allocates elements needs to do: value_alloc_type(my_allocator).allocate(n,0); Any member function which deallocates elements needs to do: value_alloc_type(my_allocator).deallocate(p); |
C++ incorporates an exception mechanism for handling unusual program events (not necessarily just errors). Exception handling enables you to detect program events and to provide handlers to deal with the events.
DEC C++ implements the C++ exception handling model described in Chapter 15 of The Annotated C++ Reference Manual (which is repeated in §r.15 of The C++ Programming Language, 2nd Edition).
This chapter provides a brief introduction to exception handling, describes the run-time considerations of exception handling in DEC C++, and recommends ways to use exception handlers for optimum performance. For a detailed description of C++ exception handling in general, see Chapter 9 and §r.15 in The C++ Programming Language, 2nd Edition.
Exception handling in DEC C++ adds no significant overhead to applications that do not use the feature. No performance cost is associated with entering or leaving a try block.
Readers of this chapter should be familiar with the C++ exception-handling terminology, such as throwing and catching exceptions.
For information on debugging programs with exception handlers, see the
DIGITAL UNIX Ladebug Debugger Manual, which is included with the operating system
documentation.
7.1 Structure
The following example shows the basic structure for declaring and using exception handlers in C++:
. . . void might_break(int i) { if (i == 1) throw "something!"; if (i == 4) throw 4; // ... } void p (int i) { // try // begin try block { // might_break(i); // } // catch (const char *p) // begin handler { // . cout << "caught " << p; // . // fix whatever... // . } // end try block } // end handler . . . |
In this example, calling p with a value of anything other than 1 or 4 causes the program to execute normally, with no exception thrown. If p is called with a value of 1, the might_break function throws a string object to the p handler, which prints caught something!.
If p is called with 4, an int is thrown. Because p cannot catch values of type int, the search for a handler proceeds up the call stack until an appropriate handler can be found. If nothing on the stack can handle an int, program execution terminates immediately after calling the terminate function.
C++ exception handling represents a termination model, which means that program execution never proceeds past a throw. For additional information, see The Annotated C++ Reference Manual.
The following example shows a hierarchy of exceptions to detect and handle:
// program fragment to illustrate exception handling // Declare the possible exceptions: class exception {}; class failure : public exception {}; class process_exception : public exception {}; class system_not_running : public process_exception {}; class no_privilege : public process_exception {}; class no_such_system : public process_exception {}; // Add other process exceptions here... // Remote_execute executes a command on a remote system void remote_execute ( const char *command, const char *system_name ) throw (process_exception); // an exception specification // The following function performs a remote execution, // waits indefinitely for a remote system to come up, and // prints error messages for other process__exception exceptions. #include <iostream.h> void protected_remote_execute ( const char *command, const char *system_name ) throw (failure) // an exception specification { try { for (;;) { try { remote_execute(command, system_name); return; } catch (system_not_running) { // Insert delay code here. continue; } } } catch (no_privilege) { cerr << "No privilege to execute process on remote system.\n"; } catch (no_such_system) { cerr << "Remote system does not exist.\n"; } catch (process_exception) { // Catch all process exceptions not dealt with above. cerr << "Remote process execution failed.\n"; } failure f; throw f; } . . . |
In this example, the inner try block
detects and handles the system_not_running exception. The outer try block detects and handles the rest of the
exceptions from the remote_execute
function. When an exception occurs in the outer try block, the handlers are searched for a
match in the order listed.
7.2 Run-Time Considerations
DEC C++ optimizes the implementation of exception handling for normal execution, as follows:
In DEC C++, a function with handlers has no intrinsic overhead. For example, functions with handlers do not have frame pointers or do not use additional registers.
Some functions without explicit handlers may have implicit handlers.
The compiler creates a handler for each automatic object that has a
destructor. The compiler also creates handlers for constructors that
initialize subobjects that have destructors. In such a constructor, the
compiler creates a handler for each member with a destructor, and a
handler for each base class with a destructor. The -nocleanup option suppresses generation of such
implicit handlers, which results in a slightly smaller executable file.
Use the -nocleanup option for programs
that do not use exception handling or do not require destruction of
automatic objects during exception processing.
7.3 Coding Recommendations
Some recommendations for optimal results when using exception handling in DEC C++ are:
. . . void foo(); volatile extern int sink; . . . void func1 (int z) { int y = 102; sink = y + y; try { int x = 88; foo(); sink = x + x; } catch (...){ { } } . . . |
When C functions are intermixed with C++ functions, DEC C++
treats them as C++ functions without exception handlers.
7.5 Hints on Using Exception Handlers
This section provides hints on using exception handling and elaborates
on the coverage of some topics in The C++ Programming Language, 2nd
Edition.
7.5.1 Propagating Changes from Exception Handlers
Changes to a caught object are not propagated if the object is caught as a value. For changes to propagate, the object must be caught as a reference. The following example shows how to propagate a change from a handler:
// In the first handler, changes made to the thrown object are not // propagated after the throw. In the third handler, changes made // to the thrown object are propagated. // (Note that throw with no operand rethrows the current exception.) extern "C" printf(...); class A { int i; public: A(int ii = 0) : i(ii) { } void display() { printf("%d\n",i); } void reset() { i = 0; } }; int main () { try { try { A a(3); // create an object throw a; // throw it } catch (A aa) // catch { aa.display(); // display current contents (3) aa.reset(); // set contents to 0 aa.display(); // display contents after reset (0) throw; // rethrow -- will not propagate } } catch(A ab) { ab.display(); // display contents -- (3) } try { try { A b(6); // create an object throw b; // throw it } catch (A &ba) // catch as a reference { ba.display(); // display current contents (6) ba.reset(); // set contents to 0 ba.display(); // display contents after reset (0) throw; // rethrow -- will propagate } } catch(A bb) { bb.display(); // display contents -- (0) } return 0; } |
If the unexpected function has an exception specification, DEC C++ calls it when a function throws an exception that is not specified in the function's definition. You can specify your own unexpected function by calling the set_unexpected function.
On a normal return from an unexpected function, DEC C++ passes control to the terminate function.
To allow your program to catch an unexpected exception, exit your unexpected function by throwing an exception. The following example shows how to return to a program from an unexpected function:
// This example shows how to return to the normal execution path by // exiting my_unexpected() with a throw. // // Output is: // // In my_unexpected(). // Caught HELP. // // Exit status is 0. #include "cxx_exception.h" extern "C" printf(...); extern "C" exit(int); typedef void(*PFV)(); PFV set_unexpected(PFV); void my_unexpected() { printf("In my_unexpected().\n"); throw; // Rethrow the current exception. } void foo() throw () // Is not allowed to throw anything { throw "HELP"; // Will result in a call to unexpected(); } int main () { // Set unexpected to be my_unexpected PFV old_unexpected = set_unexpected(my_unexpected); try { foo(); } catch(char * str) { printf("Caught %s.\n",str); } catch(...){ printf("Caught something.\n"); } set_unexpected (old_unexpected); // restore unexpected return 0; } |
On a normal return from an
unexpected
function, DEC C++ passes control to the
terminate
function. Although changes made to the
unexpected
and
terminate
functions are per thread in higher versions of DEC C++, take the
precaution of restoring the
unexpected
and
terminate
functions to their previous values upon all returns from functions that
change them using the
set_unexpected
or
set_terminate
functions.
7.5.3 Specification Conflicts
Problems can arise if conflicting exception specifications occur in function prototypes and definitions. This section describes how DEC C++ handles these conflicts. You should avoid conflicting exception specifications, especially in prototypes made available to others in header files.
According to The Annotated C++ Reference Manual, an exception specification is not considered part of a function's type. Therefore, all of the following are legal constructs:
void foo(); void foo() throw(int) { // ... } |
void foo() throw(unsigned char); void foo() { // ... } |
void foo() throw(int); void foo() throw(float) { // ... } |
If no guidelines exist regarding such conflicts, DEC C++ responds as follows:
The following example invokes a call to the unexpected function:
void foo() throw(int); // prototype exception spec ignored void foo() throw() {throw 5;} // throw 5 illegal after throw() |
The following example does not invoke a call to the unexpected function:
void foo() throw(); // prototype exception spec ignored void foo() throw(int) { throw 5; } // throw 5 legal after throw(int) |
The dlclose routine cannot be used to delete a shared object (.so) until after any handlers handling an exception, thrown from the shared object, have exited. This restriction is necessary because, when exiting from a handler, the C++ exception support needs to reference data structures that were defined at the throw point.
Previous | Next | Contents | Index |