As I explain in my other java page, I'm not very fond of the language for several reasons. This "second part" examines a bit how Java teaches bad habits to Java coders who then try to code something in C++.
When Java was designed, some inspiration was taken from C++ thus making Java similar to it in many aspects. Many syntactical details are identical or at least very similar (which gives one the feeling that they tried to make a "better C++" instead of just developing a whole new language) which makes both languages resemble each other.
I consider this a rather bad idea, specially from the point of view of someone who is fluent in Java but does not know too much about C++ and wants to (or has to) make a C++ program. Regardless of their resemblance, Java and C++ are two completely different languages and they should be approached differently; what is a good idea in one language may be a bad idea in the other. Too few people realize this fact.
Java often forces the programmer to do things in Java which are often a rather bad idea in C++. Usually there's a much simpler, easier and safer way of doing those things in C++.
Here I present two actual examples of C++ code written by Java programmers. Type, variable and function names have been changed for the sake of anonymity, and I have also added or removed whitespace for proper clearer indentation where necessary, but otherwise these are genuine unmodified pieces of code.
The most typical problem with Java coders trying to program in C++ is
that they are too infatuated with 'new
'. Here's a typical
example:
class AType { int a, b, c; [SNIP] }; class AContainer { [SNIP] }; class DerivedContainer1: public virtual AContainer { public: [SNIP] DerivedContainer1(const AType& i,const AType& e, const SomethingElse& value) : AContainer(value) { var1_ = new AType(i); var2_ = new AType(e); } [SNIP] private: AType* var1_; AType* var2_; }; typedef list<AContainer*> ContainerList; class DerivedContainer2: public virtual AContainer { public: DerivedContainer2() { thelist_ = new ContainerList(); } [SNIP] private: ContainerList* thelist_; };
In other words, the program:
AType
' to the
two pointers in 'DerivedContainer1
'. There was absolutely
no reason why these could have not been direct member variables
('AType
' contains just a couple of ints).
AContainer
' inside 'DerivedContainer2
'.
Also here there was absolutely no reason why the list could not be
a direct member variable of the class.
DerivedContainer1
' and none of any other type (by
program specification). There was no reason why the list could not
contain instances of 'DerivedContainer1
' directly.
DerivedContainer2
' dynamically
(when there was no reason to do so). Only one instance was necessary
in the program and there was absolutely no reason to allocate it
dynamically.
DerivedContainer1
' and 'DerivedContainer2
'.
In principle it was not wrong for the program to be prepared to contain
more than one derived type in that list, but in this specific case it was
completely unnecessary. In any case, allocating the 'AType
',
the list and the 'DerivedContainer2
' instances dynamically was
completely unnecessary.
This same program also had lots of member functions which returned pointers to objects dynamically allocated by the function itself, in a very Java style. These functions were public and called from other classes (that is, the responsibility of freeing those objects was transferred to them). There was, naturally, no good reason for the functions to do this.
Not surprisingly this program had quite many memory leaks even though
it tried to delete
everything it allocated.
Somehow some Java coders seem obsessed in doing things a lot more complicated than they really are:
void AClass::transform( double x, double y, double z ) { Point* tmp = new Point( x, y, z ); TransformationMatrix* matrix = new TransformationMatrix( tmp ); if( Transformations ) { Transformations->multiply( *matrix ); delete matrix; matrix = NULL; } else { Transformations = matrix; } delete tmp; tmp = NULL; return; }
The 'Transformations
' member variable is a pointer to a
dynamically allocated object of type 'TransformationMatrix
'.
With a very minor modification in that class,
'Transformations
' could be a direct member variable and
this function could be written as:
void AClass::transform( double x, double y, double z ) { Transformations.multiply(Point(x, y, z)); }
This is a lot simpler, easier, more intuitive, safer and probably quite more efficient (the compiler can probably optimize a static instance quite a lot better than a dynamically allocated one).
It's quite clear that, from the point of view of C++ programming, Java
teaches very bad habits. I believe that many Java programmers don't even
fully understand what 'new
' does (either in Java or C++).
Many probably think that it's just mandatory syntax to tell that "this
is an object of type that", that it doesn't really do anything but
is just mandatory syntax (in a similar way that eg. 'function
'
is typically a mandatory syntax in many languages to start the definition
of a function: It doesn't really do anything but to tell the
compiler that "a function definition follows").
In Java you have no choice: You either create objects dynamically with
'new
' or you don't. There's no other way. Everything is
created this way.
The big problem is that when many Java coders try to make a C++ program
they think C++ works in the same way: Everything has to be created with
'new
'. Since you can do so, and it compiles and it seems to
work, they simply do so.
But then the problems start to show up. C++ is not Java, and what is
natural in Java is often a rather bad idea in C++, and the excessive use
of 'new
' is one such thing. In Java you are forced to use
it, in C++ you should not use it unless it's absolutely necessary and
you know what you are doing.
Java programmers will then typically diss C++ because of this. They will
argument that they can freely and safely do this in Java without problems
and that C++ sucks because they can't, but they stumble accross tons of
problems because of having to call 'delete
' for each one,
they have to be careful with copy constructors and assignment operators,
etc etc.
Ok, fair enough: That is one of the weaknesses of C++: Handling dynamically allocated memory requires experience and care and should not be done lightly.
However, the problem is that more often than not you don't have
to use 'new
' at all! Unlike in Java, you can create local
instances of classes which lifetime is very well defined, and you have
standard data containers which are there so that you don't have to allocate
things yourself (most of the time the standard data containers will securely
handle allocations and deallocations themselves behind the scenes).
There are many advantages in using local instances and data containers: The code will become simpler, easier to understand, easier to write, a whole lot more secure and more efficient (both speedwise and memorywise). However, unfortunately Java teaches all the bad habits which should be avoided in C++.