Compaq C++ User Documentation — C++ 6.2
| United States |
|
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>
Adds rhs to lhs and returns the sum.
complex<T> operator+(const T& lhs, const complex<T>& rhs);template <class T>
complex<T> operator+(const complex<T>& lhs, const T& rhs);template <class T>
Subtracts rhs from lhs and returns the difference.
complex<T> operator+(const T& lhs, const complex<T>& rhs);template <class T>
complex<T> operator*(const complex<T>& lhs, const T& rhs);template <class T>
Multiplies rhs and lhs and returns the product.
complex<T> operator*(const T& lhs, const complex<T>& rhs);template <class T>
complex<T> operator/(const complex<T>& lhs, const T& rhs);template <class T>
Divides rhs into lhs and returns the quotient.
complex<T> operator/(const T& lhs, const complex<T>& rhs);template <class T>
complex<T> operator:=,=(const complex<T>& lhs, const T& rhs);template <class T>
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().
complex<T> operator:=,=(const T& lhs, const complex<T>& rhs);template <class T>
complex<T> operator!=(const complex<T>& lhs, const T& rhs);template <class T>
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().
complex<T> operator!=(const T& lhs, const complex<T>& rhs);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:
- Change #include <complex.h> or #include <complex.hxx> to #include <complex>.
- Change all declarations of complex to complex<double>, for
example:
complex c;
to:
complex<double> c;
- The polar() function no longer
supplies a default value of 0 for the second argument. Users will have
to explicitly add it to any calls that have only one argument, for
example:
complex c; c = polar(c); // get polar
to:
complex<double> c; c = polar(c,0.0);
- If you are calling a mathematical function or mathematical operator
that takes scalars as arguments (polar()
for example), then you must adjust the arguments you pass in to be the
same type as the complex template parameter type. For example, you
would have to change:
complex c = polar(0,0); complex c2 = c+1;
to:
complex<double> c = polar(0.0,0.0); // 0.0 is double complex<double> c2= c + 1.0; // 1.0 is double
- The complex_zero variable is not declared in the complex
header file. If you want to use it, you will have to declare it
yourself. For example, add the following to the top of your source file:
static const complex<double> complex_zero(0.0,0.0);
- The sqr() and arg1() functions are gone. If you want to
continue to use them, you should define them in one of your own
headers, using the following definitions.
template <class T> inline complex<T> sqr(const complex<T>& a) { return complex<T>(real(a) * real(a) - imag(a) * imag(a), 2 * real(a) * imag(a)); } template <class T> inline T arg1(const complex<T>& a) { double val = arg(a); if(val > -M_PI && val <= M_PI) return val; if(val > M_PI) return val - (2*M_PI); // val <= -PI return val + (2*M_PI); } - The pow(complex, int) function is no
longer provided. You must use pow(complex<double>, double). This means
changing calls such as:
pow(c,1);
to:
pow(c,1.0);
This may yield different results if the function previously was underflowing or overflowing, it may not happen anymore. - The complex output operator (<<) does not insert a space
between the comma and the imaginary part. If you want the space, you
would need to print the real and imaginary parts separately, adding
your own comma and space; that is:
complex<double> c; cout << "(" << c.real() << ", " << c.imag() << ")"; // add extra space - The complex input operator (>>) does not raise an Objection if bad input is detected; it instead sets input stream's state to ios::failbit.
- Floating point overflow, underflow, and divide by zero do not set errno and will cause undefined behavior. Compaq plans to do complex error checking and error notification in a subsequent release.
- You should no longer need to explicitly link your program with the complex library. It is automatically linked in as part of the standard library. You still have to explicitly link in the C math library, however, as shown in Section 6.9.1.
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:
- A private typedef to refer to the type of the allocator. The typedef uses the allocator_interface class because the allocator uses the allocator_interface class to get type information.
- A private allocator object data member.
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:
- Applications that do not use the exception feature incur no overhead.
- Applications that have handlers and that run without causing
exceptions incur only slight overhead as follows:
- The size of an executable image increases slightly, because of tables that describe the handlers.
- Code optimization is less aggressive in the presence of handlers.
- As much as possible, the overhead for exceptions is incurred when throwing an exception. Entering or leaving a try block incurs no overhead.
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:
- Use destructors where necessary, but avoid creating them where they
are not useful.
The existence of a destructor can cause the compiler to create an implicit exception handler---as in the example of an implicit exception handler for an automatic object with a destructor. - If variables are to be used only within a try block, declare them within the try block.
The following example declares and uses the variable y outside the try block, and declares and uses the variable x within the try block:
. . . 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 (...){ { } } . . . - Use fewer and larger try blocks,
instead of many small try blocks,
wherever possible.
Whereas a single try block with several handlers may improve performance compared to several small try blocks with one handler each, the improvement may not always be enough to justify making awkward changes to your program logic.
| Previous | Next | Contents | Index |
|
|
privacy and legal statement
