Java vs. C++



This post is a summary of the syntactical differences between C++ and Java.

Exception:

  • In Java, only subclass derived from Throwable can be thrown; in C++, any object can be thrown.
  • Java has two type of exceptions: checked and unchecked; all exceptions in c++ are unchecked. C++ does have exception specification. But it’s not part of function signature and it’s not checked in complied time. At runtime, if the exception specification is not obeyed, std::unexpected is called (Cf. cppreference.com Exception Specification).

Enum

  • C++’s enum is just integer internally(roughly speaking) and can be mixed with integer, and hence not type safe. C++ 11 provides a type-safe enum class (cf. wiki-enum-c++)
enum color { red, yellow, green=20, blue };
color col = red;
int n = blue; // n == 21
  • Java’s Enum is actually a special compiler-generated class rather than an arithmetic type (cf. Wiki-Enumerated_Type). A classical usage is to use Enum for factory. For example, the follow LineSearchEnum is a factory class for creating various line search methods
  public enum LineSearchEnum {
    SIMPLE {
       @Override 
       public LineSearch newInstance() {
           return new SimpleLineSearch();
       }
    }, 
    ARMIJO {
      @Override
      public LineSearch newInstance() {
          return new ArmijoLineSearch();
      }
    }, 
    CUBIC {
      @Override
      public LineSearch newInstance() {
          return new CubicLineSearch();
      }
    },
    Wolfe {
        @Override
        public LineSearch newInstance() {
            return new WolfeLineSearch();
        }
    };
    public abstract LineSearch newInstance();
}

Private inheritance

Java doesn’t support private inheritance but C++ does. Private inheritance is one way to implement the logical relation of /”is-implemented-in-terms-of”/, for example, we can implement a set using stl::list. The book Effective C++ also gives a nice example: The widget needs some functions which are already implemented in a call Timer. However, it is inappropriate for client to call Timer::onTick(). Clients of Widget should not be able to invoke anything in Timer. Widget is not a timer and the client should not be able to invoke anything in Timer directly. Public inheritance is not appropriate in this case. Private inheritance provides one possible solution. The book Effective C++ provides an alternative approach of using private class and composition. This approach has two benefits: (1) The subclass of Widget has no way to override the virtual function Timer::onTick()(note that in the first approach of private inheritance, the subclass cannot call onTick(), but can override Timer::onTick(); (2) Minimize compilation dependency if we use a pointer to WidgetTimer.

class Timer{
  pubic:
  explicit Timer(int tickFrequency);
  virtual void onTick()const;

};
class Widget:private Timer(){
 private:
  virtual void onTick() const; 
};
/*Alternative Approach*/
class Widget{
  private: 
     class WidgetTimer:public Timer{
       public: 
          virtual void onTick()const; 
     };
  WidgetTimer timer; //or use pointer WidgetTimer* timer to minimize compilation dependency; 
};

Access control

  • Java provides fourth level of access control, no modifier(package-private), public, private and protected while c++ has only three levels: public, private and protected. C++ has a concept of “friend” class. A class can access its friend’s private members.
  • Both C++ and Java have “protected” access control(cpp access control , java access control, why does the protected modifier in Java allow access to other classes in the same package), but their meaning are not the same. In C++, protected members can be accessed only by the class’s friends and its subclasses. In Java, protected members can be access also by anyclass within the package and a subclass of its class in another package. For example, the following is a valid java. In c++, subclasses change the access control when overriding. In java, subclass can also change the access control when overriding but with restriction(AccessControl and Inheritance)
          //javac -cp /Users/shiyuang/learningnotes/java/HelloWorld/classes -d $CLASSPATH/mine *.java
          /*
      Base.java -- 
       */
    package mine;
    public abstract class Base {
        public abstract void draw();
    }
    /*
      HelloWorld.java -- 
       */
    package mine;
    public class HelloWorld {
        public static void main(String[] args){
            Derived d=new Derived();
            d.draw();
        }
    }
    /*
      Derived.java -- 
       */
    package mine; 
    public class Derived extends Base {
        @Override
        protected void draw(){
            System.out.println("In Derived::draw()-3");
        }
    }
    

Constructors

  • default constructor,
    In C++, there is always a default constructor(i.e., the constructor takes no argument) if we don’t provide any constructors. In Java, this is not true. One consequence is that if there is no default constructor, and a subclass’s constructor doesn’t call super(..), there will be an compilation error. super(…) must be the first statement in subclass’s constructors.
  • Copy Constructor

c++ provides copy constructors if we don’t provide one while java doesn’t. The following java code doesn’t compile.

// filename: Main.java

class Complex {

    private double re, im;

    public Complex(double re, double im) {
        this.re = re;
        this.im = im;
    }
}

public class Main {

    public static void main(String[] args) {
        Complex c1 = new Complex(10, 15);  
        Complex c2 = new Complex(c1);  // compiler error here
    }
}

Data Hiding and the keywork final

/*
  KeywordFinal.java --
  The following example shows the problem data member is hidden by the subclass.
  In java, methods are virtual, but data is not. 
   */
class Base{
    public int a = 10; 
}
class Derived extends Base{
    public int a = 20; 
}
public class DataHiding{
    public static void print(Base obj){
        System.out.println("obj.a="+ obj.a);
        obj.a = 30; 
    }
    public static void main(String[] args){
        Derived obj = new Derived();
        print(obj); // obj.a = 10; 
        System.out.println("obj.a="+ obj.a);// obj.a = 20;
        System.out.println("obj.a="+ ((Base)obj).a);// obj.a = 30;
        System.out.println("End");
    }
}

C++ behaves exactly the same.

/*
 DataHiding.cpp -- 
*/

#include <iostream>
using namespace std;
class Base{
public:
  int a = 10;
};
class Derived: public Base{
public:
  int a = 20; 
};
void print(Base& obj){
  cout<<"obj.a="<<obj.a<<"\n";
  obj.a = 30; 
}
int main(int argc, char* argv[]){
  Derived* obj = new Derived();
  print(*obj); //obj.a = 10; 
  cout<<"obj.a="<<obj->a<<"\n"; //obj.a=20;
  cout<<"obj.a="<<((Base*)obj)->a<<"\n";//obj.a=30; 
  return 0;

In Java, we can use the keyword “final” to prevent data being hidden by the subclasses. C++ does introduce contextual keyword final. However, it is only being used for methods to prevent being overridden by subclasses or used for a class to prevent from being further derived.

“final” variables(Java) vs. const(C++)

Cf. wiki-final variables in Java. Summary

  • Unlike the value of a constant, the value of a final variable is not necessarily known at compile time. Java’s final variables must be set in initializers or constructors.
  • Java’s final variables does not guarantee immutability.
    public final Position pos;
    

    The pos cannot change but its members can unless it’s members are final too.

    const Position & pos.
    

“Final” class and override

Java provides has a concept of final class which cannot be subclassed. There are also final methods and final variables. final methods cannot be overridden or hidden by subclasses. Java also has an annotation of @Override to allow compilation to detect whether a method actually overrides anything. The keywords final and override are only introduced to C++ in C++11. They are called contextual keywords which means whether override is a keyword or not depends on the context(for example, it’s valid to name a variable to be override). Also, override is a suffix instead of prefix. The following example is from cppreference.com
#+BEGIN_SRC c++
struct A
{
virtual void foo();
void bar();
};

struct B : A
{
void foo() const override; / Error: B::foo does not override A::foo
/
(signature mismatch)
void foo() override; / OK: B::foo overrides A::foo
void bar() override; /
Error: B::bar is not virtual
};

Initializers

  • Java allows initializer one-liner or blocks. Initializers are executed whenever an instance of a class is created, regardless of which constructor is used to create the instance. The following is an example Java Initializer
  class PrimeClass
  {
      private Scanner sc = new Scanner(System.in);
      public int x;
      {
          System.out.print("Enter the starting value for x: ");
          x = sc.nextInt();
      }
}
+ C++ use /initialization list/ which is part of the constructors.

Abstract Class vs. Interface

Both Java and C++ have the concept of abstract class. But the concept is not quite the same. Abstract class cannot be instantiated. In c++, abstract class are the classes with pure virtual functions. C++ allows multiple inheritance and virtual inheritance. Java distinguishes abstract class. A class can extend at most one abstract class but implement many interface. An interface cannot have data member and all methods are public.

Method Overriding

  • In java, all instance methods are virtual by default which is not the case in c++. C++, methods can be hiding or overriding. This turns out to be a source of errors. C++ introduces a new contextual keyword override to help.

Abstract methods(Java) vs. pure virtual functions(c++)

Atn abstract method in Java can only be public or protected, but not private. In c++, it’s perfectly valid to define a private virtual function. This c++ rule seems odd at first but perfectly sensible(for example, to implement Templated Method Pattern, Cf. Effective c++ Item 35. The following example compiles and runs. Note that we can even change the access control in the subclass for pure virtual function.

#include <iostream>
using namespace std;
class Base{
public:
  //The following line will generate compilation error since a constructor cannot call a pure virtual function
  //Base(){cout<<"In Base, calling Base::print()\n"; print();}
  void testPrint(){cout<<"In Base, calling Base::print()\n"; print();}
private:
  virtual void print()=0;
};
class Derived:public Base{
public:
  Derived(){print();}
  virtual void print() override {std::cout<<"In Derived::print\n";}// Note the use of keyword override 
};
int main(int argc, char* argv[]){
  Derived d;
  d.print();
  d.testPrint();
  return 0;
}

Destructors(C++) vs. Finalizer(Java)

  • In c++ destructors are automatically call in the reverse order of construction. In java, finalizer() are not automatically chained, subclass needs to call super.final() explicitly.

Concurrency

Templating(C++) vs. Parameterized Type

  • primitive type can be passed as type parameter of a template in c++; but primitive cannot be used as type parameter in Java. Java introduced boxed primitive to resolve the problem.

Subclass Covariant

  • In c++, passing by value and passing by reference are different.

When passing by value, a single argument constructor will be called for implicit conversion (Cf. typecasting in C++). As a consequence, polymorphism is lost.

#include <iostream>
using namespace std;
class Derived;
class Base{
public:
  virtual void print(){cout<<"In Base\n";}
  Base(){cout<<"In default Base ctor\n";}
  Base(const Derived& derived) {cout<<"In non-default ctor\n";} 
};
class Derived:public Base{
public:
  virtual void print()override{cout<<"In Derived\n";}
};
void print(Base b){
  b.print();
}
int main(int argc, char* argv[]){
  Derived d;
  print(d); 
  return 0;
}

//output : In default Base ctor
// In non-default ctor
//In Base
  • In java uses the concept of covariant, and no constructor is called. It’s just like pass by reference in c++.
   class Base{
    public Base(Derived d) {System.out.println("In Base non-default ctor");}
    public Base(){System.out.println("In Base default ctor");}
    public void print(){System.out.println("In Base print ctor");}  
}
class Derived extends Base{
    public void print(){System.out.println("In Derived");}
}
public class Covariant{
    public static void print(Base obj){
        obj.print();
    }
    public static void main(String[] args){
        Derived d = new Derived();
        print(d);
    }
}
//output: In Base default ctor
//output: In Derived

Assignment =

  • In Java, the assignment means different things for primitive type and reference type. For primitive type, “=” means copy; for reference type, “=” means “bound”
/*
  assignment3.java --
  assignment means different things for primitive types and reference types.
  For primitive type, "=" means copy; for reference type, "=" means "bound"
   */

//For primitive type, "=" means copy 
public class assignment3 {
    public static void main(String[] args){
        int a = 10;
        int b = a;
        System.out.println("a=" + a); //a=20;
        System.out.println("b=" + b); // b=20;
        a=20;
        System.out.println("a=" + a); // a=20;
        System.out.println("b=" + b); // b=10;
        System.out.println("End");
    }
}

//For reference type, "=" means "bound"
class A{
    public int a =10;
}
public class assignment2 {
    public static void main(String[] args){
        A a = new A();
        A b = a;
        System.out.println("a.a:" + a.a);
        System.out.println("b.a:" + b.a);
        a.a=20;
        System.out.println("a.a:" + a.a);
        System.out.println("b.a:" + b.a);
        System.out.println("End");
    }
}
Advertisements