United States    
COMPAQ
C++
Compaq C++

Compaq C++
Using Compaq C++ for OpenVMS VAX


Previous Contents Index


Chapter 4
Porting to Compaq C++

Compaq C++ implements the C++ language as defined in The Annotated C++ Reference Manual, and closely follows that text's language definition. Strict adherence to these definitions helps you produce cleaner program code.

This chapter describes ways to avoid having the Compaq C++ compiler reject program code that previously worked with other C++ implementations that adhere less strictly to the C++ language definition. References to applicable portions of The C++ Programming Language, 2nd Edition indicate where you can find additional help.

For compatability with certain C++ compilers, Compaq C++ supports the /standard qualifier to the cxx command. This option directs the compiler to interpret the source program according to certain rules followed by other implementations of the C++ language. For more information, see Section 2.1.

4.1 Using Classes

This section discusses porting issues pertaining to C++ classes.

4.1.1 Friend Declarations

When declaring friends, use the elaborated form of type specifier. The following code fragment implements the legal declaration and comments out the illegal declaration of friend :


class Y; 
class Z; 
class X; 
   //friend Y;  ** not legal 
   friend class Z; // legal 
};      

4.1.2 Member Access

Compaq C++ enforces accessibility rules for public , protected , and private members of a base class. For more information, see §r.11.2 of The C++ Programming Language, 2nd Edition.

4.1.3 Base Class Initializers

Unlike some older C++ implementations, Compaq C++ requires you to use the base class name in the initializer for a derived class. The following code fragment implements a legal initializer and comments out an illegal initializer:


class Base { 
    // ...
public: 
    Base (int); 
}; 
class Derived : public Base { 
    // ...
public: 
    // Derived(int i) : (i)  {/* ...*/}    ** not legal        
    Derived(int i) : Base(i) {/* ...*/} // ** legal, supplies class name 
}; 

For more information, see §r.12.6.2 and §r.18.3.2 of The C++ Programming Language, 2nd Edition.

4.1.4 Undefined Global Symbols for Static Data Members

When a static data member is declared, the Compaq C++ compiler issues a reference to the external identifier in the object code, which must be resolved by a definition. On OpenVMS systems, the Compaq C++ compiler does not support the declaration anachronism shown in §r.18.3 of The C++ Programming Language, 2nd Edition. For example, consider the following code fragment:


class C { 
        static int i; 
        }; 
//missing definition 
//int C::i = 5; 
 
main () {} 

The Compaq C++ compiler does not issue any messages during compilation; however, when you attempt to link a program containing this code, the linker issues a message similar to the following:


%LINK-W-NUDFSYMS, 1 undefined symbol: 
%LINK-I-UDFSYM,         I__1C 

4.2 Using Pointers

This section demonstrates how to use pointers effectively in Compaq C++.

4.2.1 Pointer Conversions

In Compaq C++, you cannot implicitly convert a const pointer to a nonconstant pointer. For example, char ** and const char ** are not equivalent; explicitly performing such a cast can lead to unexpected results.

For more information, see §r.4.6 of The C++ Programming Language, 2nd Edition.

4.2.2 Bound Pointers

Binding a pointer to a member function with a particular object as an argument to the function is not allowed in Compaq C++. For more information on the illegality of casting bound pointers, see §18.3.4 of The C++ Programming Language, 2nd Edition.

4.2.3 Constants in Function Returns

Because the return value cannot be an lvalue, a constant in a function return has no effect on the semantics of the return. However, using a constant in a function return does affect the type signature. For example:


static int f1( int a, int b) {;} 
const int (* const (f2[])) (int a, int b) = {f1}; 

In this example, the referenced type of the pointer value f1 in the initializer for f2[] is function (signed int, signed int) returning signed int . This is incompatible with function (signed int, signed int) returning const signed int .

You can omit the const of int because it affects only the constant return signature.

4.2.4 Pointers to Constants

The following example shows a type mismatch between a pointer to a char and a pointer to a const char that some compilers, other than the Compaq C++ compiler, may not find:


void foo (const char* argv[]) {} 
int main() 
{ 
        static char* args[2] = {"foo","bar"}; 
/* 'In this statement, the referenced type of the pointer value 
 "args" is "pointer to char"' which is not compatible with 
 "pointer to const char"'*/ 
        foo (args); 
 return 0; 
} 

You can correct this example by changing static char to static const char . Use an explicit type cast to get an argument match only if no other option is available; such a cast may break on some C++ implementations.

4.3 Using typedefs

Using a synonym after a class , struct , or union prefix is illegal. Using a synonym in the names for constructors and destructors within the class declaration itself is also illegal.

In the following example, the illegal typedef specifier is commented out:


typedef struct { /* ...*/ } foo; 
// typedef struct foo foobar;             ** not legal 

For more information, see §r.7.1.3 of The C++ Programming Language, 2nd Edition.

4.4 Initializing References

Compaq C++ warns against initializing nonconstant references to refer to temporary objects. The following example demonstrates the problems that can result:


static void f() 
{ 
    int i = 5; 
    i++;        // OK 
    int &ri = 23; 
    ri++;       // In the initializer for ri, the initialization of a 
                // non-const reference requires a temporary object for "23". 
} 

The issue of reference initialization arises most often in assignment operators and in copy constructors. Wherever possible, declare all reference arguments as const .

For more information, see §r.8.4.3 of The C++ Programming Language, 2nd Edition.

4.5 Using the switch and goto Statements

Branching around a declaration with an explicit or implicit initializer is not legal, unless the declaration is in an inner block that is completely bypassed. To satisfy this constraint, enclose the declaration in a block. For example:


int i; 
 
switch (i) { 
case 1: 
    int l = 0;     //not initialized at this case label 
    myint m = 0;   //not initialized at this case label 
    { 
    int j = 0;     // legal within the braces 
    myint m = 0;   // legal within the braces 
    } 
case 2: 
    break; 
// ...
} 

For more information on using the switch statement, see §r.6.4.2 of The C++ Programming Language, 2nd Edition.

4.6 Using Volatile Objects

You must supply the meaning of copy constructing and assigning from volatile objects because the compiler generates no copy constructors or assignment operators that copy or assign from volatile objects. The following example contains examples of such errors, as noted in the comments:


class A { 
public: 
  A() { } 
  // A(volatile A&) { } 
  // operator=(volatile A&) { return 0; } 
}; 
 
void foo() 
{ 
  volatile A va; 
  A a; 
 
  A cca(va);  // error - cannot copy construct from volatile object 
  a = va;     // error - cannot assign from volatile object 
 
  return 0; 
} 

For more information, see §r.7.1.6 of The C++ Programming Language, 2nd Edition.

4.7 Preprocessing

Compaq C++ allows identifiers, but not expressions, on the #ifdef directive. For example:


// this is not legal 
// #ifdef KERNEL && !defined(__POSIX_SOURCE) 
 
// use this instead 
#if defined(KERNEL) && !defined(__POSIX_SOURCE) 

For more information, see §r.16.5 of The C++ Programming Language, 2nd Edition.

4.8 Managing Memory

The proper way to manage memory for a class is to overload the new and delete operators. This is in contrast to some older C++ implementations that let you manage memory through assignment to the this pointer.

For more information, see §r.5.3.3 and §r.5.3.4 of The C++ Programming Language, 2nd Edition. For information on how to avoid linker multiply defined symbol messages when redefining the global new and delete operators, see the description of the /prefix_library_entries qualifier in Section 1.2.1.

Make sure that user-defined new operators return pointers to memory that is quadword aligned.

4.9 Size-of-Array Argument to delete Operator

If a size-of-array argument accompanies a delete operator, Compaq C++ ignores the argument and issues a warning. The following example includes an anachronistic use of the delete operator:


int main() 
{ 
        int *a = new int [20]; 
        int *b = new int [20]; 
        delete[20] a;     //old-style; argument ignored, warning issued 
        delete[] b; 
return 0; 
} 

4.10 Flushing the Output Buffer

Do not depend on the newline character (\ n ) to flush your terminal output buffer. If you want to flush the output buffer, use the endl manipulator or the flush member function.

4.11 Missing Parenthesis Error Message

Situations occur in which a simple typographical error generates a missing parenthesis error message. In the following example, the class name CaseSensitive is incorrectly specified as Casesensitive in the constructor declaration:


class CaseSensitive { 
    void Test( const Casesensitive &foo ); 
}; 

As the compiler parses the argument declaration list, it first sees const , which it interprets as a type specifier. The compiler then sees Casesensitive , which it interprets as a dname . Among the next legal tokens are the equal sign, comma, and closing parenthesis. Upon finding an ampersand, the compiler expects a closing parenthesis. With all other possibilities exhausted, the compiler has what appears to be a legal argument declaration list, after which the closing parenthesis is the only allowable token. The compiler expected one thing but encountered something else. Often, inserting newline characters can isolate the offending token.


Previous Next Contents Index
  

1.800.AT.COMPAQ

privacy and legal statement