United States    
COMPAQ STORE | PRODUCTS |
SERVICES | SUPPORT | CONTACT US | SEARCH
cxxtitle.gif (12116 bytes)
Compaq C++

Compaq C++
Using Compaq C++ for Tru64 UNIX Systems


Previous Contents Index

6.9.4 Complex Math Nonmember Operators

template <class T>
complex<T> operator+(const complex<T>& lhs, const T& rhs);

template <class T>
complex<T> operator+(const T& lhs, const complex<T>& rhs);

Adds rhs to lhs and returns the sum.

template <class T>
complex<T> operator+(const complex<T>& lhs, const T& rhs);

template <class T>
complex<T> operator+(const T& lhs, const complex<T>& rhs);

Subtracts rhs from lhs and returns the difference.

template <class T>
complex<T> operator*(const complex<T>& lhs, const T& rhs);

template <class T>
complex<T> operator*(const T& lhs, const complex<T>& rhs);

Multiplies rhs and lhs and returns the product.

template <class T>
complex<T> operator/(const complex<T>& lhs, const T& rhs);

template <class T>
complex<T> operator/(const T& lhs, const complex<T>& rhs);

Divides rhs into lhs and returns the quotient.

template <class T>
complex<T> operator:=,=(const complex<T>& lhs, const T& rhs);

template <class T>
complex<T> operator:=,=(const T& lhs, const complex<T>& rhs);

Compares lhs to rhs and returns true if they are equal, otherwise returns false. The imaginary parts of the scalars are assumed to be the value T().

template <class T>
complex<T> operator!=(const complex<T>& lhs, const T& rhs);

template <class T>
complex<T> operator!=(const T& lhs, const complex<T>& rhs);

Compares lhs to rhs and returns true if they are not equal, otherwise returns false. The imaginary parts of the scalars are assumed to be T().

template <class T, class charT, class traits>

istream& operator>>(istream& is, complex<T>& c);

Extracts a complex number c from the istream is of the form u, (u), or (u,v), where u is the real part and v is the imaginary part. If bad input is encountered, sets ios::failbit.

template <class T, class charT, class traits>

ostream operator<<(ostream& os, const complex<T>& c);

Returns os << "(" << c.real() << "," << c.imag() << ")".

6.9.5 Typedefs

typedef T value_type;

The type used to represent the real and imaginary parts of the complex number.

6.9.6 Unsafe Downcasts

Because the Compaq C++ compiler does not yet support the explicit keyword (that is, nonconverting constructors), unsafe downcasts will be allowed; that is, users can convert a complex<double> to a complex<float>.

6.9.7 Upgrading from the Nonstandard Complex Math Library

This section explains how to upgrade from the pre-ANSI Compaq C++ complex library to the current Compaq C++ standard complex library, which is based on the 24 September 1996 ANSI C++ working draft.

In the ANSI library, complex objects are templatized on the type of the real and imaginary parts, the pre-ANSI Compaq C++ library, complex objects are not templatized. The pre-ANSI library assumes the type is double, whereas the new library provides specializations for float, double, and long double as well as allowing users to specialize on their own floating point types.

Also note that mathematical error checking is not supported in the ANSI library. Users who rely on detection of underflow, overflow, and divide by zero should continue using the pre-ANSI Compaq C++ complex library.

The following is a detailed list of important changes:

6.10 The Allocator Class

The Standard C++ Library defines an allocator class that is used to control the memory model containers will use. Every container class in the Standard C++ library takes an allocator as one of its template arguments. In this way each container can manage its own storage requirements. Containers can make use of different memory models by using allocators that implement different types of memory management schemes. A default allocator that makes use of the global new and delete is provided by the Standard library.

The allocator as defined in the 24 September 1996 draft C++ Standard makes use of member function templates and member class templates. The lack of member template support in the current version of Compaq C++ mandates the use of an allocator that does not utilize this feature. Therefore the Compaq C++ library provides an alternate allocator class. The alternate allocator allocates raw bytes of storage. Its types and functions are provided by an allocator_interface class. Under this scheme the allocator makes use of the allocator_interface class to allocate typed storage rather than allocating itself directly. The implementation is not standard conforming so its use will be documented in this section.

Note that the current version of the Compaq C++ library also contains a specialization for void. The specialization for void does not conform to standard either. Refer to the 24 September 1996 ANSI C++ draft for details on the ANSI C++ draft conforming allocator.

The rest of this section defines the allocator class provided with the current version of Compaq C++. It also describes how to build custom allocators and containers that make use of this allocator class.

6.10.1 Allocator Class Member Functions

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.

6.10.2 The allocator_interface Class

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 { 
    //... 
} 

6.10.3 The allocator_interface Member Functions

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.

6.10.4 Designing Customized Allocators

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 Compaq 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> 
 

6.10.5 Using Custom Allocators with Standard Library Containers

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; 
 
 

6.10.6 Implementing Custom Containers that Use the Allocator

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); 


Chapter 7
Handling Exceptions

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.

Compaq 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 Compaq 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 Compaq 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 Tru64 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

Compaq C++ optimizes the implementation of exception handling for normal execution, as follows:

In Compaq 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 Compaq C++ are:


Previous Next Contents Index
  

1.800.AT.COMPAQ

privacy and legal statement