Thursday 30 March 2017

C++ Reference Parameters Versus Java Reference Parameters & Converting C++ Abstract Classes into Java Interfaces - Java Tutorials

C++ Reference Parameters Versus Java Reference Parameters

In the preceding section, you saw an example of a C++ program that used a pointer parameter. In Java, this became a reference parameter. Of course, C++ also supports reference parameters. As mentioned, most pointer parameters found in C++ code are simply holdovers from C. Nearly all new C++ code will use reference parameters when a function needs access to the argument, itself. (In essence, pointer parameters, although still common, are actually anachronisms in most C++ code.) Since both Java and C++ support reference parameters, you might think that the conversion of a C++ function that uses reference parameters to a Java method would involve few changes. Unfortunately, this is not always the case. To understand why, let’s convert the following C++ program, which swaps the contents of two Coord objects using reference parameters:

  // Swap coordinates -- C++ version.
  #include <iostream>
  using namespace std;

  class Coord {
  public:
    int x;
    int y;
  };

  // Swap contents of two Coord objects.
  void swap(Coord &a, Coord &b) {
    Coord temp;

    // swap contents of objects
    temp = a;
    a = b;
    b = temp;
  }
  int main()
  {
    Coord ob1, ob2;
    
    ob1.x = 10;
    ob1.y = 20;

    ob2.x = 88;
    ob2.y = 99;

    cout << "Original values:\n";
    cout << "ob1: " << ob1.x << ", " << ob1.y << "\n";
    cout << "ob2: " << ob2.x << ", " << ob2.y << "\n";
    cout << "\n";
    swap(ob1, ob2);

    cout << "Swapped values:\n";
    cout << "ob1: " << ob1.x << ", " << ob1.y << "\n";
    cout << "ob2: " << ob2.x << ", " << ob2.y << "\n";

    return 0;
  }

Following is the output produced by this program. As you can see, the contents of ob1 and ob2 have been exchanged:

  Original values:
  ob1: 10, 20
  ob2: 88, 99

  Swapped values:
  ob1: 88, 99
  ob2: 10, 20

In Java, all objects are accessed via an object reference variable. Thus, when an object is passed to a method, only its reference is passed. This means that all objects are automatically passed by reference to a Java method. Without thinking any deeper about what is actually occurring, someone might initially try the following (incorrect) conversion of the preceding program:

  // Swap program incorrectly converted to Java.
  class Coord {
    int x;
    int y;
  };

  class SwapDemo {
    static void swap(Coord a, Coord b) {
      Coord temp = new Coord();

      // this won't swap contents of a and b!
      temp = a;
      a = b;
      b = temp;
    }

    public static void main(String args[]) {
      Coord ob1 = new Coord();
      Coord ob2 = new Coord();

      ob1.x = 10;
      ob1.y = 20;

      ob2.x = 88;
      ob2.y = 99;

      System.out.println("Original values:");
      System.out.println("ob1: " +
                         ob1.x + ", " + ob1.y);
      System.out.println("ob2: " +
                         ob2.x + ", " + ob2.y + "\n");

      swap(ob1, ob2);

      System.out.println("Swapped values:");
      System.out.println("ob1: " +
                         ob1.x + ", " + ob1.y);
      System.out.println("ob2: " +
                         ob2.x + ", " + ob2.y + "\n");
    }
  }

The output produced by this incorrect program is shown here:

  Original values:
  ob1: 10, 20
  ob2: 88, 99

  Swapped values:
  ob1: 10, 20
  ob2: 88, 99

As you can see, the values of ob1 and ob2 in main( ) have not been exchanged! Although a bit counterintuitive at first, the reason is actually obvious, once you understand precisely what happens when an object reference is passed to a method. Java passes all arguments to methods using call-by-value. This means that a copy of the argument is made, and what occurs to the copy inside the method has no effect on the argument used to call the method. However, this situation is blurred a bit in the case of object references.

When an object reference is passed to a method, a copy of the reference variable is made, as just explained. This means that the parameter inside the method will refer to the same object as does the reference variable used as an argument outside the method. Therefore, operations on the object through the parameter will affect the object referred to by the argument (since they are one and the same). But operations on the reference parameter, itself, affect only that parameter. Thus, when the preceding program attempts to swap the objects by exchanging the objects pointed to by a and b, all that is happening is that the parameters (that is, the copies of the arguments) are exchanging what they are referring to, but this does not alter what ob1 and ob2 refer to back in main( ).

To fix the program, swap( ) needs to be rewritten so that the contents of the objects are exchanged, not what the parameters refer to. Here is the corrected version of swap( ):

  // Corrected version of swap().
  static void swap(Coord a, Coord b) {
    Coord temp = new Coord();

    // swap contents of objects
    temp.x = a.x;
    temp.y = a.y;
    a.x = b.x;
    a.y = b.y;
    b.x = temp.x;
    b.y = temp.y;
  }

If you substitute this version of swap( ) into the preceding program, the correct results will be achieved.




Converting C++ Abstract Classes into Java Interfaces

One of the most innovative aspects of Java is the interface. As explained earlier in this book, an interface specifies the form of its various methods without specifying any implementation details. Each class that implements an interface does so by creating the actual methods declared by the interface. Thus, in Java an interface is the means by which you can define the general form of a class while ensuring that all specific versions of the class conform to the same set of rules. The interface is one of the ways that Java provides support for polymorphism.

In C++, there is no direct parallel to the interface. Instead, in C++, if you wish to define the form of a class without defining implementation details, you must do so by using an abstract class. Abstract classes in C++ are similar to abstract classes in Java: they do not contain a full set of implementation details. In C++, an abstract class contains at least one pure virtual function. A pure virtual function defines no implementation; it only defines the function prototype. Thus, a pure virtual function in C++ is essentially the same as an abstract method in Java. In C++, abstract classes serve a function similar to interfaces in Java. For this reason, they are one of the items that you will want to watch for when converting code to Java. While not all C++ abstract classes can be converted into Java interfaces, many can. Let’s look at two examples.

Here is a short C++ program that uses an abstract class called IntList to define the form of an integer list. An implementation of this class is created by IntArray, which uses an array to implement a list of integers.

  // A C++-style abstract class and its implementation.
  #include <iostream>
  #include <cstdlib>
  using namespace std;

  // An abstract class that defines the form of an integer list.
  class IntList {
  public:
    virtual int getNext() = 0; // pure virtual functions
    virtual void putOnList(int i) = 0;
  };

  // Create an implementation of an integer list.
  class IntArray : public IntList {
    int storage[100];
    int putIndex, getIndex;
  public:
    IntArray() {
      putIndex = 0;
      getIndex = 0;
    }

    // Return next integer in list.
    int getNext() {
      if(getIndex >= 100) {
        cout << "List Underflow";
        exit(1);
      }
      getIndex++;
      return storage[getIndex-1];
    }

    // Put an integer on the list.
    void putOnList(int i) {
      if(putIndex < 100) {
        storage[putIndex] = i;
        putIndex++;
      }
      else {
        cout << "List Overflow";
        exit(1);
      }
    }
  };

  int main()
  {
    IntArray nums;
    int i;

    for(i=0; i<10; i++) nums.putOnList(i);

    for(i=0; i<10; i++)
      cout << nums.getNext() << endl;

    return 0;
  }

In this program, the abstract class IntList defines only the form of an integer list. It contains only pure virtual functions and does not declare any data. For these reasons, it can be made into an interface when the program is converted into Java, as shown here:

  // Here, IntList is made into an interface which IntArray
                                                   implements.
  // Define interface for an integer list.
  interface IntListIF {
    int getNext();
    void putOnList(int i);
  }

  // Create an implementation of an integer list.
  class IntArray implements IntListIF {
    private int storage[];
    private int putIndex, getIndex;

    IntArray() {
      storage = new int[100];
      putIndex = 0;
      getIndex = 0;
    }

    // Create an implementation of an integer list.
    public int getNext() {
      if(getIndex >= 100) {
        System.out.println("List Underflow");
        System.exit(1);
      }
      getIndex++;
      return storage[getIndex-1];
    }

    // Put an integer on the list.
    public void putOnList(int i) {
      if(putIndex < 100) {
        storage[putIndex] = i;
        putIndex++;
      }
      else {
        System.out.println("List Overflow");
        System.exit(1);
      }
    }
  }

  class ListDemo {
    public static void main(String args[]) {
      IntArray nums = new IntArray();
      int i;

      for(i=0; i<10; i++) nums.putOnList(i);

      for(i=0; i<10; i++)
        System.out.println(nums.getNext());
    }
  }

As you can see, there is nearly a one-to-one correspondence between the C++ abstract class IntList and the Java interface IntListIF. It is possible to convert IntList into IntListIF because it contained only pure virtual functions. This is the key. If IntList had contained any data or function implementations, then it would not have qualified for conversion into an interface.

When you convert or adapt C++ code into Java, look for examples of abstract classes that contain only pure virtual functions. These are prime candidates for conversion to Java interfaces. But don’t overlook abstract C++ classes that contain a small number of implemented functions or data. It is possible that these items don’t really belong in the abstract class to begin with and should be defined by individual implementations. Since C++ does not define an interface construct, there was no reason
for C++ programmers to think in terms of one.

Sometimes a concrete member is contained in an otherwise abstract class simply for expedience—not because it is the most logical place for it. For example, consider the following abstract C++ class:

  // An abstract C++ class.
  class SomeClass {
    bool isOK;
  public:
    virtual int f1() = 0;
    virtual void f2(int i) = 0;
    virtual double f3() = 0;
    virtual int f4(int a, char ch) = 0;
  };

The only reason that this class cannot be made into a Java interface is the existence of isOK. Presumably, isOK is used to indicate some status associated with the class. However, if you think about it, there really is no reason for isOK to be defined as a variable. Instead, you could specify a method called isOK( ) that returns the status. In this approach, isOK( ) will be defined, along with the other methods, by any implementing class. Thus, you could convert the preceding C++ abstract class into the following Java interface:

  interface SomeClass {
    int f1();
    void f2(int i);
    double f3();
    int f4(int a, char ch);
    boolean isOK();
  }
Many abstract classes in C++ can—and should—be converted into interfaces when you move code to Java. In doing so, you will probably find that it clarifies the structure of the class hierarchy.

No comments:

Post a Comment