United States    
COMPAQ STORE | PRODUCTS | SERVICES | SUPPORT
| CONTACT US | SEARCH
C++
Compaq C++

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


Previous Contents Index


Chapter 2
Compaq C++ Implementation

This chapter discusses the features and characteristics specific to the Compaq C++ implementation, including pragmas, predefined names, numerical limits, and other implementation-dependent aspects of the language definition.

2.1 Compatibility with Other C++ Compilers

The Compaq C++ compiler implements the language definition as specified in The Annotated C++ Reference Manual, which is the basis of the currently proposed ANSI C++ standard. This section describes specific accommodations to make programs developed using other C++ products compile more easily under Compaq C++.

To enhance compatibility with other C++ compilers, Compaq C++ supports options that direct the compiler to interpret the source program according to certain rules followed by other implementations. The currently supported options are:

Although these options direct the Compaq C++ compiler to interpret your source code according to conventions used by other C++ implementations, the compatibility with these implementations is not complete. The following sections describe the compatibility situations that Compaq C++ recognizes. For clarity, these sections are marked to indicate which compatibility option supports the change in interpretation: -cfront or -ms .

2.1.1 Incomplete friend Declaration (cfront and ms)

Allow the reserved word class , struct , or union to be omitted from a friend statement (contrary to the syntax rules for C++).

For example:


class foo; 
class goo { 
    friend foo; //"class" is missing; compatibility options allow this 
}; 

2.1.2 Enumerators in an Enumeration Declaration (cfront and ms)

Ignore any extra comma at the end of the last enumerator in an enumeration declaration.

For example:


enum E {a, b, c,};  // Ignore the "," after "c", if -cfront or -ms is specified 

2.1.3 Incomplete Declarations (cfront and ms)

When a class type declaration is left incomplete at the end of a local scope, the incomplete type declaration is hoisted to its outer scope to allow the type to be completed by any subsequent declaration.

For example:


void bar(struct foo *f); // foo is incomplete at the end of the prototype scope, 
                         // so hoist it out to the file scope. 
 
struct foo { int i; };   // Now, foo can be completed by this declaration. 
 
void bar(struct foo *f)  // Now, this foo is the same as the one above. 
{  
    f->i = 1; 
} 
 
void main (void) { 
    foo r; 
 
    r.i = 2; 
    bar(&r);             // Now, type of r is the same as type of f in bar. 
} 

Another example:


typedef struct st { 
    struct test *ptr;  // test is left incomplete at the end of the scope, 
                       // so hoist it out to the file scope. 
} st; 
 
typedef struct test {  // Now, test is allowed to be completed. 
    int a; 
} test; 
 
void init_st (st *o) 
{ 
    o->ptr->a = 3;     // Now, the ptr field is considered to be the same 
                       // type as test just above. 
} 
 
void main (void) { 
    st r; 
    test t; 
 
    r.ptr = &t; 
    t.a = 1; 
    init_st(&r); 
} 

2.1.4 Overloading Resolution and Enumeration Value of Zero (cfront)

When performing overloading resolution, prefer conversion of an enum type object of 0 value to an arithmetic type, to conversion to a pointer type.

For example:


void trans(unsigned int input);  // The first trans 
void trans(char* re);            // The second trans 
 
enum inputs{nickle, dime}; 
 
void f(void) 
{ 
    trans(nickle); // nickle's value is 0, so cfront prefers the first trans 
} 

2.1.5 Prefix and Postfix Increment and Decrement Operators (cfront)

When no operator++(int) (or operator--(int) ) are visible, then invoke operator++() (or operator--() ) for postfix increment (or postfix decrement) operations.

The language specifies that the prefix increment and decrement user-defined operators take one argument (the implicit this argument for a member function), whereas the postfix version takes two arguments (an extra int argument).

For example:


class foo 
{ 
    public: 
    int operator++(); 
//  int operator++(int);  
    int operator--(); 
//  int operator--(int);  
}; 
 
void f(void) 
{ 
    foo j; 
 
    j++; 
    ++j; 
    j--; 
    --j; 
} 

When -cfront is specified, the j++ and ++j operations both call int operator++() . Also, the j-- and --j operations call int operator--() . But, if you uncomment int operator++(int) and int operator--(int) in the class declaration, then the language-specified operations will be called whether -cfront is specified or not.

2.1.6 Explicit Cast to Reference and Conversions (cfront and ms)

Call any constructors or conversion functions as a result of an explicit cast to a reference (contrary to §r.5.4 of the The C++ Programming Language, 2nd Edition).

For example:


extern "C" void printf(char *, ...); 
 
struct S2; 
 
struct S1 
{ 
    operator S2&() {printf("%s \n", "operator S2& called");} 
}; 
 
struct S2 : public S1 
{ 
    print(){printf("%s \n", "print called");} 
}; 
 
void f(S1 &rs1) 
{ 
    ((S2 &)rs1).print();  // I know this is a pointer to S2. 
} 
 
void main(void) 
{ 
    S2 s2; 
 
    f(s2); 
} 

Without the -cfront option, running the program will print:


print called 

But, with the -cfront option, running the program will print:


operator S2& called 
print called 

2.1.7 Access Rights to Enclosing Class (cfront)

Allow members of an inner class to have the same access rights to the enclosing class as members or friends of the enclosing class.

For example:


class X { 
    protected: 
 class Y { 
 }; 
 class Z { 
     X::Y *y;  // Ok: specifying -cfront makes X::Y accessible 
 }; 
}; 

2.1.8 Static Incomplete Array Data Members (ms)

Allow static array data members with incomplete class components. For example:


struct x; 
struct c; 
{ 
   static x a[];  //component x is incomplete, -ms option allows this 
}; 
struct x 
{ 
   int j; 
} 

2.1.9 Address of Member Functions without Ampersand (&) Address-of Operator (ms)

When supplying the qualified name of a member function in a pointer-to-member context, permit the ampersand (&) address-of operator to be omitted. For example:


class C { 
public: 
    int f(); 
}; 
 
int (C::*pmf1)() = C::f;        // with -ms, OK to refer to member function 
                                // without address-of operator 
int (C::*pmf2)() = &C::f;       // without -ms, ampersand must be supplied 

2.1.10 Ignored Visual C++ Keywords (ms)

Recognize certain Visual C++ keywords (such as export ) but ignore them.

2.1.11 Duplication of Type Qualifiers (ms)

Allow the duplication of type qualifiers. For example:


const const int i;  

2.2 Implementation-Specific Attributes

This section describes pragmas, predefined names, and limits placed on the number of characters and arguments used in Compaq C++ programs.

2.2.1 #pragma Preprocessor Directive

The #pragma preprocessor directive is a standard method for implementing features that differ from one compiler to the next. This section describes pragmas specifically implemented in the Compaq C++ compiler for Tru64 UNIX systems.

Although certain #pragma directives are subject to macro expansion, not all of those new to the language in this and later releases are subject to such expansion. Therefore, users should not write programs that rely on macro expansion of the newer pragmas.

2.2.1.1 #pragma define_template Directive

The #pragma define_template preprocessor directive instructs the compiler to define a template with the arguments specified in the pragma. This pragma has the following syntax:

#pragma define_template name < template-argument-list >

For example, the following statement instructs the compiler to define the template mytempl with the arguments arg1 and arg2 :


#pragma define_template mytempl<arg1, arg2> 

For more information on how to use templates with the #pragma
define_template directive, see Section 5.3.

2.2.1.2 #pragma environment Directive

The #pragma environment preprocessor directive offers a way to single-handedly set, save, or restore the states of context pragmas. This directive protects include files from contexts set by encompassing programs and protects encompassing programs from contexts that could be set in header files that they include.

On Tru64 UNIX systems, the #pragma environment directive affects the following pragmas:

#pragma member_alignment
#pragma message
#pragma extern_model
#pragma pointer_size
#pragma required_vptr_size

This pragma has the following syntax:

#pragma environment command_line
#pragma environment header_defaults
#pragma environment restore
#pragma environment save

command_line

Sets, as specified on the command line, the states of all the context pragmas. You can use this pragma to protect header files from environment pragmas that take effect before the header file is included.

header_defaults

Sets the states of all the context pragmas to their default values for Tru64 UNIX systems. This is almost equivalent to the situation in which a program with no command line options and no pragmas is compiled; except that this pragma sets the pragma message state to #pragma nostandard , as is appropriate for system header files.

restore

Restores the current state of every pragma that has an associated context.

save

Saves the current state of every pragma that has an associated context.

Someone who creates a general purpose library, distributed as an archive or shared library, typically distributes header files that specify the interface to the library. For calls to the library to work, the header file must normally use the same member alignment, pointer size, extern model, and other compilation options as when the library was built. That header file should contain pragmas to save the user's compilation options, set the correct compilation options for the library, and then restore the user's compilation options when the include file ends. The pragmas let library users choose whatever compilation options are appropriate for their own code without unintentionally affecting the interface to the library.

Not only is the #pragma environment preprocessor directive more convenient than explicitly specifying each individual pragma that controls a compilation option, it is also upwardly compatible. As new pragmas are added to Compaq C++, #pragma environment header_defaults will be enhanced to set the state of the new pragmas to their default. Thus, a header file that uses #pragma environment sets a known state for not only the current pragmas in Compaq C++, but future pragmas as well. Without requiring further changes to the source code, you can use #pragma environment to protect header files from things like language extensions and enhancements that might introduce additional contexts.

A header file can selectively inherit the state of a pragma from the including file and then use additional pragmas as needed to set the compilation to non-default states. For example:


#ifdef __PRAGMA_ENVIRONMENT 
#pragma __environment save  (1)
#pragma __environment header_defaults (2)
#pragma member_alignment restore (3)
#pragma member_alignment save (4)
#endif 
. 
.  /* contents of header file */ 
. 
#ifdef __PRAGMA_ENVIRONMENT 
#pragma __environment restore 
#endif 

In this example:

  1. Saves the state of all context pragmas
  2. Sets the default compilation environment
  3. Pops the member alignment context from the #pragma member_alignment stack that was pushed by #pragma __environment save (restoring the member alignment context to its pre-existing state)
  4. Pushes the member alignment context back onto the stack so that the #pragma __environment restore can pop the entry off

Thus, the header file is protected from all pragmas, except for the member alignment context that the header file was meant to inherit.

2.2.1.3 #pragma [no]member_alignment Directive

By default, the Compaq C++ compiler aligns structure members so that members are stored on the next boundary appropriate to the type of the member; that is, bytes are on the next byte boundary, words are on the next word boundary, and so on.

You can use the #pragma member_alignment preprocessor directive to explicitly specify member alignment. For example, using #pragma member_alignment aligns a long variable on the next longword boundary, and it aligns a short variable on the next word boundary.

Using #pragma nomember_alignment causes the compiler to align structure members on the next byte boundary regardless of the type of the member. The only exception to this is for bit-field members.

If used, the nomember_alignment pragma remains in effect until the compiler encounters the member_alignment pragma.

This pragma has the following syntax:

#pragma member_alignment
#pragma member_alignment save
#pragma member_alignment restore
#pragma nomember_alignment [base_alignment]

restore

Restores the current setting of the member_alignment pragma.

save

Saves the current setting of the member_alignment pragma.

2.2.1.4 #pragma message Directive

The #pragma message preprocessor directive controls the kinds of individual diagnostic messages or groups of messages that the compiler issues.

The #pragma message directive has the following syntax:

#pragma message disable (message-list)
#pragma message enable (message-list)
#pragma message restore
#pragma message save

disable

Supresses the compiler-issued messages specified in the message-list argument. The message-list argument can be any one of the following:

The message identifier is the name following the message text when the -verbose option is set. For example, consider the following message:


Non-void function "name" does not contain a return statement. (missingreturn). 

The message identifier is missingreturn . To prevent the compiler from issuing this message, use the following directive:


#pragma message disable missingreturn 

enable

Enables the compiler to issue the messages specified in the message-list argument.

restore

Restores the saved state of enabling or disabling compiler messages.

save

Saves the current state of enabling or disabling compiler messages.

The save and restore options are useful primarily within header files.

2.2.1.5 #pragma pack Directive

The #pragma pack preprocessor directive specifies the byte boundary for packing members of C structures and C++ classes.

The #pragma pack directive has the following format:

#pragma pack [(n)]

n specifies the new alignment restriction in bytes as follows:
1 Align to byte
2 Align to word
4 Align to longword
8 Align to quadword
16 Align to octaword

A structure member is aligned to either the alignment specified by #pragma pack or the alignment determined by the size of the structure member, whichever is smaller. For example, a short variable in a structure gets byte-aligned if #pragma pack (1) is specified. If #pragma pack (2) , (4) , or (8) is specified, the short variable in the structure gets aligned to word.

If #pragma pack is not used, or if (n) is omitted, packing defaults to 8 on Tru64 UNIX systems.

2.2.1.6 #pragma pointer_size Directive

The #pragma pointer_size preprocessor directive controls pointer size allocation for the following:

For this pragma to have any effect, you must specify -xtaso , -xtaso_short , -vptr_size , or -vptr_size_short on the cxx command.

This pragma has the following syntax:

#pragma pointer_size {long|64}
#pragma pointer_size {short|32}
#pragma pointer_size restore
#pragma pointer_size save

long, or 64

Sets as 64 bits all pointer sizes in declarations that follow this directive, until the compiler encounters another #pragma pointer_size directive.

short, or 32

Sets as 32 bits all pointer sizes in declarations that follow this directive, until the compiler encounters another #pragma pointer_size directive.

restore

Restores the saved pointer size from the pointer size stack.

save

Saves the current pointer size on a pointer size stack.

The save and restore option are particularly useful for specifying mixed pointer support and for protecting header files that interface to older objects. Objects compiled with multiple pointer size pragmas will not be compatible with old objects, and the compiler cannot discern that incompatible objects are being mixed.

Use of short pointers is restricted to Compaq C++ and the C compilers resident on Tru64 UNIX systems. Programs should not attempt to pass short pointers from C++ routines to routines written in any other language except the C programming language. Also, Compaq C++ may require explicit conversion of short pointers to long pointers in applications that use short pointers. You should first port those applications in which you are considering using short pointers, and then analyze them to determine if short pointers would be beneficial.

A difference in the size of a pointer in a function declaration is not sufficient to overload a function.

Compaq C++ issues an error-level diagnostic if:

2.2.1.7 #pragma required_pointer_size Directive

The #pragma required_pointer_size preprocessor directive controls pointer size allocation in the same way as #pragma pointer_size but without interaction with command-line options. This pragma is always enabled, whether or not you specify any pointer size options on the command line. The same syntax, precautions, and restrictions pertain as with #pragma pointer_size . Neither the pragma name nor its arguments are subject to macro expansion.

2.2.1.8 #pragma required_vptr_size Directive

The #pragma required_vptr_size preprocessor directive controls pointer size allocation in the same way as #pragma required_pointer_size , but it applies to virtual function pointers and virtual bases in a C++ class object. This pragma has the following syntax:

#pragma required_vptr_size {long|64}
#pragma required_vptr_size {short|32}
#pragma required_vptr_size restore
#pragma required_vptr_size save

The parameters have the same meaning as with #pragma required_pointer_size (see Section 2.2.1.6).

The #pragma required_vptr_size directive takes effect at the opening brace of a class declaration. This pragma is always enabled, whether or not you specify any pointer size options on the command line.

2.2.2 Predefined Names (§r.16.10)

Table 2-1 lists the predefined C++ macros used by Compaq C++. For information on using predefined macros in header files in the common language environment, see Section 3.1.

Table 2-1 Predefined Macros
Macro Description
__DATE__ A string literal containing the date of the translation in the form Mmm dd yyyy , or Mmm d yyyy if the value of the date is less than 10
__FILE__ A string literal containing the name of the source file being compiled
__TIME__ A string literal containing the time of the translation in the form of hh:mm:ss
__LINE__ A decimal constant containing the current line number in the C++ source file

Table 2-2 lists other names predefined by Compaq C++.

(
Table 2-2 Other Predefined Names
Name  
__cplusplus Language identification name.
__DECCXX Language identification name.
__INITIAL_POINTER_SIZE A decimal constant containing the initial pointer size allocation, as specified by the command line. Valid values are: 0 (no pointer size option set), 32 (short or 32-bit pointers allocated), and 64 (long or 64-bit pointers allocated).

On Tru64 UNIX systems, Compaq C++ supports the predefined macro names described in Table 2-3.

Table 2-3 Predefined Macros Specific to Tru64 UNIX Systems
Name Description
__alpha System identification name
__host_alpha System identification name
__osf__ System identification name
__unix System identification name
__unix__ System identification name

C++ programmers using both Tru64 UNIX and OpenVMS Alpha operating systems should use the predefined macro __alpha for code that is intended to be portable from one system to the other.

Predefined Implementation Compatibility Macros

When the -cfront command line option is specified, Compaq C++ predefines __CFRONT . When the -ms command line option is specified, Compaq C++ predefines __MS .

Predefined Version Number Macro

Compaq C++ defines the predefined macro __DECCXX_VER . You can use this macro to test that the current compiler version is newer than a particular version. Newer versions of the compiler always have larger values for this macro. If for any reason the version cannot be analyzed by the compiler, then the predefined macro is defined but has the value of 0. Note that releases of Compaq C++ prior to Version 5.0 do not define this macro, so you can distinguish earlier compiler versions by checking to determine if the __DECCXX_VER macro is defined.

The following example tests for Compaq C++ Version 5.1 or higher:


#ifdef __DECCXX_VER 
    #if __DECCXX_VER >= 50100000 
        / *Code */ 
    #endif 
#endif 

2.2.3 Translation Limits

The only translation limits imposed in Compaq C++ are as follows:
Limit Meaning
32,767 Characters in an internal identifier or macro name.
32,767 Characters in a logical or physical source line.
32,767 Bytes in the representation of a string literal. This limit does not apply to string literals formed by concatenation.
32,767 Significant characters in an external identifier. A warning is issued if such an identifier is truncated.
1024 Arguments in a function call. 1
1024 Parameters in a function definition. 1
1012 Bytes in any one function argument.


1The compiler may add one or two hidden arguments to a function, which reduces to 1023 or 1022 the number of arguments available to the user.

2.2.4 Numerical Limits

The numerical limits, as defined in the header files <limits.h> and <float.h> , are as follows:

Numerical limits not described in this list are defined in The Annotated C++ Reference Manual.

2.2.5 Argument-Passing and Return Mechanisms

Compaq C++ passes arrays, functions, and class objects with a constructor or destructor by reference. All other objects are passed by value.

If a class has a constructor or a destructor, it is not passed by value. In this case, the compiler calls a copy constructor to copy the object to a temporary location, and passes the address of that location to the called function.

If the return value of a function is a class that has defined a constructor or destructor or is greater than 64 bits, storage is allocated by the caller and the address to this storage is passed in the first parameter to the called function. The called function uses the storage provided to construct the return value.

2.3 Implementation Extensions and Features

This section describes the extensions and implementation-specific features of Compaq C++ for Tru64 UNIX systems. Where appropriate, section numbers shown in parentheses in the headings (for example, §3.4) refer to relevant sections in the reference manual portion of The C++ Programming Language, 2nd Edition.

2.3.1 Identifiers (§r.2.3)

In Compaq C++, the dollar sign ($) is a valid character in an identifier.

For each external function with C++ linkage, the compiler decorates the function name with a representation of the function's type.

2.3.1.1 External Name Encoding

The Compaq C++ compiler uses the external name encoding scheme described in §7.2.1c of The Annotated C++ Reference Manual, with certain modifications. These modifications are necessary so that the encoding scheme does not depend on case distinction in linker names.

For the basic types, the external name encoding scheme is exactly the same as that described in The Annotated C++ Reference Manual, as follows:
Type Encoding
void v
char c
short s
int i
long l
float f
double d
long double r
... e

Class names are encoded as described in The Annotated C++ Reference Manual, except that the Compaq C++ compiler uses the lowercase q instead of uppercase Q , and denotes the qualifier count as a decimal number followed by an underscore, as follows:
Class Notation Encoding
simple Complex 7Complex
qualified X::YY q2_1x2yy

Type modifiers are encoded as follows:
Modifier Encoding
const k
signed g
volatile w
unsigned u
__unaligned b

Type declarators are encoded as follows:
Type Notation Encoding
array [10] a10_
function () x
pointer * p
pointer to member S::* m1S
reference & n
unnamed enumeration type h

On Tru64 UNIX systems, Compaq C++ also supports the following data types:
Type Encoding
__int16 ji4
__int32 ji5
__int64 ji6
__s_float js
__t_float jt

2.3.1.2 Character Limit for Long Names

If an external name has more than 1022 characters, the Compaq C++ compiler truncates the name to 1022 characters. The compiler keeps the first 1015 characters intact, reduces (hashes) the remaining characters to a string of 7 characters, and appends the 7 hashed characters to the first 1015.

2.3.2 Order of Static Object Initialization (§r.3.4)

Static objects are initialized in declaration order within a compilation unit and in link order across compilation units.

2.3.3 Integral Conversions (§r.4.2)

When demoting an integer to a signed integer, if the value is too large to be represented, the result is truncated and the high-order bits are discarded.

Conversions between signed and unsigned integers of the same size involve no representation change.

2.3.4 Floating-Point Conversions (§r.4.3 and §r.4.4)

When converting an integer to a floating-point number that cannot exactly represent the original value, Compaq C++ rounds off the result of the conversion to the nearest value that can be represented exactly.

When the result of converting a floating-point number to an integer or other floating-point number at compile time cannot be represented, the Compaq C++ compiler issues a diagnostic message.

When converting an integral number or a double floating-point number to a floating-point number that cannot exactly represent the original value, Compaq C++ rounds off the result to the nearest value of type float .

When demoting a double value to float , if the converted value is within range but cannot exactly represent the original value, Compaq C++ rounds off the result to the nearest representable float value.

Compaq C++ performs similar rounding for demotions from long double to double or float .

2.3.5 Explicit Type Conversion (§r.5.2.3)

In Compaq C++, the expression T() (where T is a simple type specifier) creates an rvalue of the specified type, whose value is determined by default initialization. According to The C++ Programming Language, 2nd Edition, the behavior is undefined if the type is not a class with a constructor, but the ANSI working draft removes this restriction. With this change you can now write:


    int i=int(); // i must be initialized to 0 

2.3.6 The sizeof Operator (§r.5.3.2)

The type of the sizeof operator is size_t . In the header file <stddef.h> , this type is defined as unsigned long , which is the type of the integer that holds the maximum size of an object.

2.3.7 Explicit Type Conversion (§r.5.4)

A pointer takes up the same amount of memory storage as objects of type long (or the unsigned equivalent). Therefore, a pointer can convert to any of these types and back again without changing its value. No scaling occurs and the representation of the value is unchanged.

Conversions to and from a shorter integer and a pointer are similar to conversions to and from a shorter integer and unsigned long . If the shorter integer type was signed, conversion fills the high-order bits of the pointer with copies of the sign bit.

2.3.8 Multiplicative Operators (§r.5.6)

The semantics of the division (/) and remainder (%) operator are as follows:

In the following cases of undefined behavior detected at compile time, the Compaq C++ compiler issues a warning:

Integer overflow
Division by 0
Remainder by 0

2.3.9 Additive Operators (§r.5.7)

You can subtract pointers to members of the same array. The result is the number of elements between the two array members, and is of type ptrdiff_t . In the header file <stddef.h> , Compaq C++ defines this type as long .

2.3.10 Shift Operators (§r.5.8)

The expression E1 >> E2 shifts E1 to the right E2 positions. If E1 has a signed type, Compaq C++ fills the vacated high-order bits of the shifted value E1 with a copy of E1 's sign bit (arithmetic shift).

2.3.11 Equality Operators (§r.5.10)

When comparing two pointers to members, the Compaq C++ compiler guarantees equality if either of the following conditions hold:

When comparing two pointers to members, the Compaq C++ compiler guarantees inequality if either of the following conditions hold:

When created by different address expressions, two pointers to members may compare either as equal or as unequal if they produce the same member when applied to the same object.

2.3.12 Type Specifiers (§r.7.1.6)

For variables that are modifiable in ways unknown to the Compaq C++ compiler, use the volatile type specifier. Declaring an object to be volatile means that every reference to the object in the source code results in a reference to memory in the object code.

2.3.13 asm Declarations (§r.7.3)

In Compaq C++, asm declarations produce a compile-time error.

2.3.14 Linkage Specifications (§r.7.4)

Specifying linkage other than "C++" or "C" generates a compile-time error.

In object files, Compaq C++ decorates with type information the names of functions with C++ linkage. This permits overloading and provides rudimentary type checking across compilation units. The type-encoding algorithm used is similar to that given in §7.2.1c of The Annotated C++ Reference Manual (see Section 2.3.1.1).

2.3.15 Class Layout (§r.9.2)

The alignment requirements and sizes of structure components affect the structure's alignment and size. A structure can begin on any byte boundary and can occupy any integral number of bytes.

2.3.15.1 Structure Alignment

Structure alignment represents the maximum alignment required by any member within the structure. When the structure or union is a member of an array, padding is added to ensure that the size of a record, in bytes, is a multiple of its alignment.

Components of a structure are laid out in memory in the order in which they are declared. The first component has the same address as the entire structure. Padding is inserted between components to satisfy alignment requirements of individual components.

2.3.15.2 Bit-Fields

The presence of bit-fields causes the alignment of the whole structure or union to be at least the same as that of the bit-field's base type.

For bit-fields (including zero-length bit-fields) not immediately declared following other bit-fields, their base type imposes the alignment requirements (less than that of type int ). Within the alignment unit (of the same size as the bit-field's base type), bit-fields are allocated from low order to high order. If a bit-field immediately follows another bit-field, the bits are packed into adjacent space in the same unit, if sufficient space remains; otherwise, padding is inserted at the end of the first bit-field and the second bit-field is put into the next unit.

Bit-fields of base type char must be smaller than 8 bits. Bit-fields of base type short must be smaller than 16 bits.

2.3.15.3 Access Specifiers

The layout of a class is unaffected by the presence of access specifiers.

2.3.15.4 Class Subobject Offsets

A class object that has one or more base classes contains instances of its base classes as subobjects. The offsets of nonvirtual base class subobjects are less than the offsets of any data members that are not part of base class subobjects.

The offsets of nonvirtual base classes increase in derivation order. The offset of the first nonvirtual base class subobject of any class is 0. For single inheritance, the address of a class object is always the same as the address of its base class subobject.

If a class has virtual functions, an object of that class contains a pointer to a virtual function table (VFPTR). If a class has virtual base classes, an object of that class contains a pointer to a virtual base class table (VBPTR). For a class with no base classes, the offset of a VFPTR or VBPTR is greater than the offset of any data members. Thus, the offset of the first data member of a class with no base classes is 0, which facilitates interoperability with other languages. If the leftmost base class of a class with base classes has a VFPTR, a VBPTR, or both, and is not virtual, the class and its base class share the table or tables.

The offsets of virtual base class subobjects are greater than the offset of any data member, and increase in the order of derivation of the virtual base classes. In increasing order, a class object contains the following:

  1. Nonvirtual base class subobjects
  2. Data members
  3. VFPTR (if required)
  4. VBPTR (if required)
  5. Virtual base class subobjects

Consider the following example:


class B1 
{ 
 int x[1]; 
}; 
class B2 : virtual B1 
{ 
 int y[2]; 
 virtual int fl(); 
}; 
class B3 : virtual B2, virtual B1 
{ 
 int z[3]; 
 virtual int f2(); 
}; 
class D : B3 
{ 
 int a[4]; 
 virtual int f1(), f2(), f3(); 
}; 

Figure 2-1 shows the layout of an object of D class for this example.

Figure 2-1 Layout of an Object of D Class


2.3.16 Multiple Base Classes (§r.10.1)

Within a class object, base class subobjects are allocated in derivation order; that is, immediate base classes are allocated in the order in which they appear in the class declaration.

2.3.17 Temporary Objects (§r.12.2)

Under the following conditions, the compiler creates temporary objects for class objects with constructors:

Variations in the compiler generation of such temporary objects can adversely affect their reliability in user programs. The compiler avoids introducing a temporary object whenever it discovers that the temporary object is not needed for accurate compilation. Therefore, you should modify or write your programs so as not to depend on side effects in the constructors or destructors of temporary objects.

2.3.17.1 Lifetime of Temporary Objects

Generally Compaq C++ implements destruction of temporary objects at the ends of statements. In certain situations, however, temporary objects are destroyed at the end of the expression; they do not persist to the end of the statement. Temporary objects do not persist to the ends of statements in expressions that are:

Consider the following example:


struct A { 
  void print(int i); 
  A(); 
  ~A() { } 
}; 
 
struct B { 
  A* find(int i); 
  B(int i); 
  B(); 
  ~B() { } 
}; 
 
void f() { 
  B(8).find(6)->print(6); 
  (*(B(5).find(3))).print(3); 
  return; 
} 

In the first and second statements inside void f() , Compaq C++ destroys the temporary object, created in evaluating the expressions B(8) and B(5) , after the call to A::print(int) .

2.3.17.2 Nonconstant Reference Initialization with a Temporary Object

If your program tries to initialize a nonconstant reference with a temporary object, the compiler generates a warning. For example:


struct A { 
  A(int); 
}; 
void f(A& ar); 
 
void g() { 
  f(5);  // warning!! 
} 

2.3.17.3 Static Member Functions Selected by Expressions Creating Temporary Objects

When a static member is accessed through a member access operator, the expression on the left side of the dot (.) or right arrow (->) is not evaluated. In such cases, the compiler creates code that calls the static member function to handle the destruction of a class type temporary; the compiler does not create temporary destructor code. For example:


struct A { 
        ~A(); 
        static void sf(); 
}; 
 
struct B { 
        A operator ()() const; 
}; 
 
void f () { 
    B bobj; 
    bobj().sf();        // If 'bobj()' is evaluated, a temporary of 
                        // type 'A' is created. 
} 

2.3.18 Exception Handling (§r.15)

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

In Compaq C++, a procedure with handlers has no intrinsic overhead. For example, procedures with handlers do not have frame pointers or additional register usage.

Some procedures 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 an executable file that is slightly smaller. You should use this option for programs that do not use exception handling or that do not require destruction of automatic objects during exception processing.

Exception specifications in function prototypes that conflict with function definitions are illegal. Chapter 7 describes how Compaq C++ handles such conflicts. Beware of such conflicting exception specifications, particularly if you have exception specifications in prototypes that are in header files accessible to other programs.

2.3.19 File Inclusion (§r.16.4)

The #include directive inserts external text into the macro stream delivered to the Compaq C++ compiler. Programmers often use this directive to include global definitions for use with Compaq C++ functions and macros in the program stream.

The #include directive has the following search path semantics:


Previous Next Contents Index
  

1.800.AT.COMPAQ

privacy and legal statement