I realize The Internet doesn’t need another rant about which programming language is better, but I need to write one, apparently. Here is why I prefer C++ to Java in various categories.
Portability
If you ask a random person on the street which of C++ or Java is more portable, they’ll probably say “Java”. But it’s really not that simple.
Java is the language of choice for Android. Android is pretty ubiquitous these days. However there’s a big difference between an Android app and a Java applet. Your Android app probably won’t run on your PC, so when somebody says “Java is portable”, I don’t know what they’re talking about.
C++ needs to be compiled into processor-specific assembly, so you have to compile your C++ into as many libraries as architectures you support. SO WHAT? It’s not much more difficult to compile for one architecture as it is to compile for a few. Also, intel-based Android devices run ARM assembly. Most games made in Unity only have one ARM version of the binary in them, and nobody seems to be losing any sleep.
Garbage Collection
Another advantage of Java is garbage collection. Early iPhone apps had a lot of memory leaks because developers didn’t understand the objective-C memory allocation paradigm. Mobile OS’s tend (for some reason) to have an unclear concept of quitting an app. So, maybe it’s a good idea to have garbage collection on mobile.
Personally, I’ve never felt like garbage collection actually helped me very much. Yeah, memory leaks are elusive, but so are a lot of bugs. And garbage collection doesn’t actually make memory leaks impossible, it just makes them invisible. It replaces the thought process of “when would I like this object to be deallocated?” with “how is it that I’m retaining a reference to this object without realizing it?” Admittedly in Java this contemplation doesn’t happen as often, but it’s harder to deal with when it does.
On Android, each Java class is implemented in an underlying C++ class. Sometimes, finalize()
gets used to deallocate that underlying C++ object. If there's ever a bug with this, it becomes a weird race-y bug that's nearly impossible to find. For example, the issue described here:
http://stackoverflow.com/questions/30879831/garbage-collection-invalidates-filedescriptor
It's hard to tell at first that a bug is connected to garbage collection because garbage collection passes are (hopefully) rare. I suppose this is not a fault with Java the language though. So, let's talk about language features.
Const
C and C++ have const
. const
can be used in a number of places in C++, but in particular, you can make a method const
like this:
class MusicNote
{
void draw() const;
// ...
};
Here const
forbids draw()
from changing member variables of MusicNote
and from calling non-const methods. A lot of bugs in software come from programmers not realizing all the states the objects can be in. It’s helpful to be able to look at a function’s prototype and tell whether it might be changing the state of that object. In Java you can use final
to get some of the uses of const
, but not this very important one. For instance, you can put final
here:
final int NUM_STEREO_CHANNELS = 2;
and it does something similar to C++. But in this context:
final void close();
it forbids the function from being overridden.
I get the logic of that, and it’s a good idea to have a mechanism for forbidding a subclass from overriding a particular function. That way, a class can defend against subclasses accidentally sabotaging it. In C++, this is the role of virtual
. Again, I prefer the C++ way, because the default behavior is the more defensive one.
People sometimes complain that C++ has too many exceptions to its own rules. “const
is great except I can’t trust it because of mutable
.” I understand this, but mutable
has its place. When a const
function caches intermediate results, for instance. Or a graphics function like this:
void display() const
{
// ... graphics code...
}
which changes the state of an object in a retain-mode graphics API. Java doesn't have mutable
because it doesn't have const
. Everybody can just change everything they can see. Which brings us to visibility.
Visibility
Java and C++ both have public
, private
and protected
. The basic functionality in the two languages is the same, but the Devil is in the details.
Java has a fourth visibility mode “package”. A lot of people don't realize this at first, because there’s no “package” keyword, instead package visibility is attained by default if the visibility modifier is omitted. My first complaint about this is that it’s confusing for beginners.
I understand the need for something like package visibility, but I still prefer the C++ way which is to use friend
. friend
allows one class to permit another class access to private and protected members. Again, this is an area where proponents of Java would say C++ has too many exceptions to its own rules. So, why do I like this? Because sometimes the way classes interface with each other needs to be more intimate than the way the client of those classes interfaces with them. Maybe you have
class List
{
//...
}
class Iterator
{
//...
}
It’s easy to imagine List
and Iterator
needing access to members of each other that are invisible to the client. The Java way of doing this would be to put List
and Iterator
in the same package and use package visibility. But everything in the package can see an element with package visibility…. Maybe the package is company wide… Also, there's a Java convention where the directory structure of files reflects the package structure, so if I change my mind about package organization, then I have to move the files!
Also, C++ allows a class to declare a visibility modifier on its parent, i.e.
class MyIntList : private list<int>
{
void push();
void pop();
// ...
};
This makes even the public
and protected
members of the parent list<int>
accessible only from within MyIntList functions. That way, if MyIntList makes assumptions in its use of the parent, it can assure those assumptions won’t be sabotaged by an outside caller.
Pass-By-Reference / Operator Override
In Java, primitives are passed by copy, and objects are passed by reference. That’s fairly easy to get used to, and a lot of other languages are like that.
int A = 4;
int B = 3;
A = B;
The code above does the same thing In C++ and Java. A and B represent different words in memory, and assigning B to A copies the word represented by B into A’s location. They are still different, but identical in content.
String A = "apple";
String B = "banana";
A = B;
In this code, when interpreted as Java, A and B first reference different objects, but then, after the assignment, A and B reference the same object.
The same code in C++ (assuming a class called String) is actually more similar to the previous example with ints. A and B represent different spaces in memory, one of whose contents gets copied to the other.
I find the Java way slightly inconsistent, but Python, Ruby and JavaScript are also that way, so fine, I guess.
The place where I really miss the C++ scheme is in something like a mathematical vector class.
class Vector
{
double x;
double y;
double z;
// ...
}
Combined with operator override (another C++ feature Java doesn’t have) you can make code that looks like this
Vector midPoint(Vector A, Vector B)
{
return 0.5 * (A + B);
}
The equivalent function in Java would look something like this:
public Vector midPoint(Vector A, Vector B)
{
return A.add(B).multiply(0.5);
}
Objects pass by reference, so the Java midPoint
has to call new
twice. Am I the only one who finds that wasteful? Will the temporary Vector
that Java makes when this function executes get deallocated at the end of the function? Or will it get deallocated at some arbitrary point in the future when garbage collection runs? I'm not sure.
I have more to say, but I have to get to work.