C++ for Java Programmers

Barbara Staudt Lerner
September 1998


Modified by Bill Lenhart
September 2002


C++ was developed in the early 1980's. Its goal was to introduce object-orientation to C while maintaining backwards compatibility so that existing C programs would continue to work without change. Java, developed in the early 1990's inherited none of that baggage and instead was intent on developing a pure object-oriented language with syntactic similarities to C.

Besides the desire for backwards compatibility, C++ also maintained the philosophy that performance was critical. Many design decisions of C++ were done to allow programmers to get maximum performance out of their programs at the cost of making the code more difficult to understand and more error-prone.

Java was originally developed as a programming language for programs embedded in electronic devices, such as microwave ovens, CD players, telephones, etc. Since this was a new marketplace, there was not a lot of legacy code that the Java designers needed to maintain compatibility with. Also, since the performance of architectures has improved substantially and the computing demands of these devices were not great, performance of the language was much less of a concern, but productivity of the programmers was considered quite important.

The end result is that Java is much more programmer-friendly than C++. James Gosling, the creator of Java, quips that Java is C++ without the guns, knives, and clubs. We will try to avoid most of the dangerous weapons. This document should give you a basic understanding of the subset of C++ needed for your graphics work. This should help you read the C++ code that I provide. You might want to supplement this with a C++ text to get all the details. This document assumes that you already know Java.

Differences among Features Common to C++ and Java

Low-level Syntax

At the level of statements and declarations, C++ and Java are quite similar. The syntax that you have learned in Java will, for the most part, carry over to C++. C++ includes more constructs than Java, for which you will need to learn both the syntax and semantics.

The boolean data type consisting of the values true and false is called bool in C++.

In C++, the data type char is an 8 bit value capable of representing an ASCII character. An instance of the char type can also be treated as an 8 bit integer. Therefore, you can do arithmetic on variables declared as char.

In C++, you can declare any of the numeric data types to be unsigned. This results in the lower bound for the type being 0. For example, int normally ranges from -231 to 231. An unsigned int ranges from 0 to 232.

Unsigned types are often used for specifying color information in the form of RGB values for color lookup tables. Note: In Java, the sizes of the built-in types are defined by the language. For example, an int is always 32 bits long. In C++ this is not the case. Many of the numeric types are implementation-defined. In C++, = is the assignment operator just as in Java. In C++, = can also be used as an expression that returns the value being assigned. This allows the following convenient way of initializing two variables to the same value:

    a = b = 1;
1 is assigned to b. The assignment expression returns the value assigned and assigns this to a.

C++ allows the condition controlling an if-statement or while-statement to be an integer expression. If the integer expression evaluates to 0, this is treated the same as the boolean value false. A non-zero value is treated the same as true. I prefer to write

    if (someInt != 0)
rather than
    if (someInt)
even though they are semantically equivalent.

Combining these last two paragraphs shows one of the most common syntactic errors in C++ programs:

    int a = 0, b = 1;
    if (a = b) {
      ...
    }
Assume that the programmer intended to say a == b as the condition, which is almost certainly the case. The programmer therefore intended that the body of the if-statement would be executed only if a and b had the same value. In Java, the code above would give a compilation error, but it does not in C++ since = is an expression. Instead the value of b is assigned to a, so a now becomes 1. This value (1) is returned as the result of the assignment expression. Since integer expressions are allowed for conditions, this is ok. 1 is treated as true and the body of the if-statement is executed, which is not what the programmer intended. Be on the lookout for this simple error in your code!

In C++, you must declare the size of an array when you declare the array (actually, some compilers are more lenient---but you shouldn't depend on it). Memory is allocated for the array when the array is declared:

    int intArray[10];
Beware! C++ does not check array bounds like Java does. If you pass in a negative number for an array bound or an array bound that is greater than the size of the array, C++ will happily access some (seemingly random) piece of memory. If this appears on the left side of an assignment statement, it will happily change some (seemingly random) piece of memory. Always check array bounds yourself if you are not absolutely certain that the value is in the correct range!!!

In C++, it is possible to declare that a variable should be kept in a register. This is typically done to improve performance but is really unnecessary with modern compilers. Compilers are smart about recognizing which variables are used most often and keeping those values in registers. If not used extremely carefully, register declarations actually degrade performance. avoid using them for now, but for the curious, here's the syntax.

    register int i;

Classes

Both Java and C++ use classes as an abstraction mechanism. Classes encapsulate data and methods that operate on that data. In C++, a class definition is broken into a declaration and a separate definition of each member. It is not possible to attach the keywords public, private, or protected to the class definition. If the class is public (in the Java sense), you put its declaration in a separate file whose name ends in .h, while you put the definitions in a file with the same name but ending in .cc. A class declaration can be broken into three sections: a public section, a protected section, and a private section. Instead of attaching these keywords to each class member as in Java, you put the member in the appropriate section of the declaration as follows:
    class Pair {
      public:
        Pair (int x, int y);  // The constructor
        int getX();
        int getY();
        void setX (int newValue);
        void setY (int newValue);
    
      private:
        int x;
        int y;
    };

Also, note that C++ requires a semicolon at the end of a class declaration.

To make this class visible to another file, it must be included in that file:

    #include <pair.h>
While packages do not exist in C++, there is a related concept called namespace. Because namespace was not supported und the version of g++ used when developing the code for this course, I am not planning to use this feature.

The member definitions appear in the .cc file as mentioned earlier. Since they appear outside the class declaration, each definition needs to declare which class it is in using the class_name:: syntax:

    int Pair::getX() {
      return x;
    }
Any members that are fully defined in the class declaration (usually just the variables) should not be defined in the .cc file.

If you want to specify a particular member from a specific class when doing a function call, for example, you use the :: operator, as in

    SomeClass::someMethod()

This syntax is used toaccess static member functions of classes, and member functions of ancestor classes. The syntax for declaring a subclass is different in C++:

    class SubClass : public SuperClass {
      ...
    };
This is a declaration of SubClass as a subclass of SuperClass. Note that you must include the keyword public before the superclass name. If you want to allow a method to be overridden in a subclass, the superclass must include the keyword virtual in its declaration of the method. Any class that contains a virtual function and no definition of that function is implicitly abstract. It is not possible to declare a class or function to be abstract. There is no equivalent to Java's super keyword. If you want to refer to a superclass function that is overridden in a subclass, you must explicitly qualify the function name with the class from which it is inherited using the :: syntax.

Input and Output

There are several ways to do input and output in C++. The first way is using the scanf and printf functions. I am not going to say another word about them, because there is a much better way: the iostream classes. The preferred way of doing input and output in C++ is using streams. To use streams, you must put #include in your program in order to include the appropriate header file. Among many features provided are default input and output streams cin and cout. A simple code fragment illustrates their use:
    int a;
    float x;
    cout << "Enter an int and a float separated by whitespace: "
    cin >> a >> x;
    cout << "You entered " << a << " and " << x << ", correct?" << endl;
This produces the following when run:
    Enter an int and a float separated by whitespace: 3 7.1
    You entered 3 and 7.1, correct?
where the 3 and the 7.1 on the first line would have been typed by the user.

Features Unique to C++

Constants

To declare a value to be constant in C++, you use const (not final) as in:
    const int MAX_SIZE 10
C and C++ also provide a powerful (read: easy to misuse) macro facility using the #define keyword. Don't use it in this course unless specifically instructed. This is just the tip of the const iceberg. More information will follow as needed.

Compiler Directives

#include and #define are examples of compiler preprocessor commands. These are commands that are executed by a preprocessor that scans the code prior to compilation. The preprocessor is run automatically when you run the compiler. Two other common directives in C++ are #ifdef and #ifndef. #ifdef takes a variable name for its condition. If that variable name is defined, it evaluates to true and its body is included in the source code that is compiled. #ifndef is similar but includes its body if the variable is not defined. Both may have #else clauses. They both end with the delimiter #endif.
    #ifdef HOST_SPARC
    #include 
    #endif
This is how C++ programmers typically port programs between architectures. Architecture-dependent code is placed inside #ifdef statements. When the code is compiled, the appropriate variable is set for the architecture allowing the correct code to be compiled in. Unlike normal if-statements, these if-statements are evaluated at compilation time. The branch that is true at compilation time is compiled into the program. Branches that are false are not compiled in. The condition is not tested at runtime.

The only use we will make of these macros is to guarantee that header files are not included more than once in a program. Suppose, by way of example, you have a class called Sphere3D. You would put the class declaration in the file Sphere3D.h and the implementation in the file Sphere3D.cc. Then any user of your Sphere3D class would put the line

    #include "Sphere3D.h"
in their programs, often in the header files of their own classes. However, the C++ compiler becomes very sad if it stumbles across the contents of Sphere3D.h (or any header file) more than once, so we need a way of guaranteeing that the contents of header files are not 'included' more than once. This is done by wrapping the body of each header file in the following:
    #ifndef SPHERE3D_H
    #define SPHERE3D_H
    // Your header file code goes here
    #endif SPHERE3D_H
Using SPHERE3D_H as the 'macro variable' name is just a tradition. All that matters is that different header files use different names. This guarantees that no two header files use the same string in the #ifndef statement. How does this work?

When the preprocessor first includes a file with three lines like this, SPHERE3D_H is not yet defined, and so all of the code between the #ifndef and #endif macros (i.e. the actual class declaration, for example) is included. In particular, the #define macro is included (and processed) so that SPHERE3D_H is now defined. During subsequent preprocessing, if the same file is included again, SPHERE3D_H will have already been defined and so the code between the #ifndef and #endif macros will not be included again. (By the way, "ifndef" is short for "if not defined".)

Life Outside of a Class

In Java, everything is declared inside of a class. Since C++ needed to maintain backwards compatibility with C, this is not true for C++. Data types, variables, and functions can all be declared outside of classes. These are referred to by simply using their names. There is no . syntax required to dereference them.

If a data declaration is to be global and shared between multiple files, it is declared in one file and declared to be an extern in the other files. We all know that global variables are bad, so we shouldn't do this. The main program for a C++ program is called main, but it is declared externally to any class. Its signature is:

int main (void); // or you could say int main();

Struct Types

Note: There should be no need for you to use this feature--- create a class instead.

Data type declarations outside of classes are encapsulated inside a struct:

struct Date {
  char *month;
  int date;
  int year;
};

struct Date someDate;

Typically, when declaring a type one gives the type a name. Oddly enough, creates a type named struct Date. To give it a simple type name, a slightly different syntax is required:

typedef struct {
  char *month;
  int date;
  int year;
} Date;

Date someDate;

Enumerated Types

The ability to create an enumerated type is one feature of C++ that I really wish Java had. With an enumerated type, you can define your own type with its own set of discrete values. For example,
enum RenderMode {
  wireFrame,
  solid,
  rayTrace
};

RenderMode rm;
rm = rayTrace;

Enumerated types are simulated in Java in the following way. The programmer declares a class (equivalent to the enumerated type) that provides a number of public constants (representing the enumerated values).

Union Types

Note: There should be no need for you to use this feature

A union type is a type that allows a particular piece of memory to store a value of different types at different types (a primitive precursor to subtyping). A union declaration looks a lot like a struct declaration:

union String_or_int {
  char *someString;
  int someInt;
};
The union itself does not keep track of which type is in it, so a union is typically used inside a struct where a second field of the struct remembers the type currently in the union field:
struct S_or_i {
  bool containsInt;
  union String_or_int x;
};

Pointers

In Java, all references to objects are pointers to objects. All references to primitive types, like int are values. In C++, using a type name always means that the variable will have a value of that type. It is possible to introduce pointers to values explicitly and also to create types whose values are pointers to other values. Suppose we have a Date type, here is how we would declare a variable that is to contain a pointer to a date and also a type to represent a pointer to a date:
    Date *someDate;         // Variable containing a pointer to a Date

    typedef Date *DatePtr;  // Type defining a pointer to a Date
    DatePtr date2;	    // Variable containing a pointer to a Date

    date2 = someDate;
someDate and date2 both contain pointers to dates. The assignment statement results in both variables pointing to the same memory location and therefore sharing the same value as happens in Java.

Contrast the above with the following similar code that does not use pointers:

    Date someDate;
    Date date2;

    date2 = someDate;
Assuming that Date is simply a struct type, not a pointer type, the assignment statement above copies the value from someDate to date2. If the value referenced in either variable is changed, it has no effect on the other value. In Java, you would need to explicitly clone the value to have this effect. Unless you know the definition of the type involved in an assignment, you cannot tell whether the assignment results in value-sharing or value-copying.

A pointer is dereferenced using the -> syntax:

    some_pointer->some_field
In C++, this is a pointer, not an object. To dereference it, you must say
    this->member

To get a pointer to an object, you use the & operator:

    int *IntPtr;
    int anInt;

    anInt = 1;
    intPtr = &anInt;

To get the value pointed to by a pointer, you use the * operator:

    int *intPtr;
    int anInt, int2;

    anInt = 1;
    intPtr = &anInt;
    int2 = *intPtr;

In C++, all parameters are passed by value. If you want to be able to change the value of a parameter as a side effect, you must declare the parameter type to be a pointer and you must pass in the address of the variable that you want to change:

    void increment (int * anInt) {
        (*anInt)++;
    }

    int i = 0;
    increment (&i);

If you want to pass a pointer or an array to a function, but you do not want the object to be changed, you can say that the parameter type is const:

    void doSomething (const int * anInt) {...}

    int *intArray;
    // Assume the array has been allocated and given memory.
    doSomething (intArray);

Memory Management

Java is a garbage-collected language. C++ is not. In Java, memory is allocated for an object when that object is constructed. The memory is deallocated when there are no more references to that object.

In C++, objects (and structs) can either be automatic or manually allocated. Variables whose types are not pointers (such as classes or structs) are automatically allocated and deallocated. They are allocated when they are declared and deallocated at the end of the block in which they are declared. You do not use new to allocate a variable whose type is a class.

With pointer types, the programmer must explicitly allocate and deallocate memory. You must allocate memory before assigning a value to the object. Allocation for classes is done with new as in Java. You should deallocate an object when you believe there are no more references to that object. The syntax for deallocating an object is:

    delete list1;
where list1 is the name of an object. If you have an array of objects, and you want to delete all the objects in the array, say
    delete [] objArray;
For every object allocated with new there should be a deallocation with delete.

If you want to do anything special when deleting an object, you must define a deconstructor for the object's class. A typical thing to do is to delete the objects referenced by the object being deleted (if you are sure they are the last reference to that object!). The syntax for declaring a deconstructor is:

    ~MyClass();
where MyClass is the name of the class containing the deconstructor.

Similarity between Arrays and Pointers

Suppose you want to have an array variable, but you do not know how big the array should be. Since C++ requires you to declare the size of the array when you declare the array variable, you cannot declare it to be an array. Instead you must declare a pointer to the desired element type and later allocate the appropriate amount of memory yourself:
    int *intArray;
    intArray = new int[10]
    // or
    Sphere *sphereArray = new Sphere[maxSize];
Even though you declared the variable to be a pointer, you can still dereference it as an array!

Features Unique to Java

There is no equivalent to the synchronized keyword. Threads are also not built into the language.

There is no equivalent of JavaDoc for C++.

C++ does not have an instanceof operator, but it does have a function dynamic_cast which takes a type T and a pointer p, and returns a non-zero value if p is of a class type derived from (inheriting) type T.

C++ does not have interfaces; multiple inheritance is used.

Many C++ implementations come with a collection of useful classes called the standard library (sometimes called the Standard Template Library or STL. This library provides strings, streams, some basic data structures, as well as other features. We will discuss these as we need them.


Last modified by Bill Lenhart on September 10, 2002.