C++ | Java |
---|---|
const Foo *a; // it is not possible to modify the object
// pointed to by a through a
| final Foo a; // a declaration of a "final" reference:
// it is possible to modify the object,
// but the reference will constantly point
// to the first object assigned to it
|
a = new Foo();
| a = new Foo(); // Only in constructor
|
a->x = 5;
// ILLEGAL
| a.x = 5;
// LEGAL, the object's members can still be modified
// unless explicitly declared final in the declaring class
|
Foo *const b = new Foo();
// a declaration of a "const" pointer
| final Foo b = new Foo();
// a declaration of a "final" reference
|
b = new Foo();
//ILLEGAL, it is not allowed to re-bind it
| b = new Foo();
// ILLEGAL, it is not allowed to re-bind it
|
b->x = 5;
// LEGAL, the object can still be modified
| b.x = 5; // LEGAL, the object can still be modified |
- C++ supports
goto
statements, which may lead to Spaghetti programming. With the exception of the goto statement (which is very rarely seen in real code and highly discouraged), both Java and C++ have basically the same control flow structures, designed to enforce structured control flow, and relies on break and continue statements to provide somegoto
-like functionality. Some commenters point out that these labelled flow control statements break the single point-of-exit property of structured programming.
- C++ provides low-level features which Java lacks. In C++, pointers can be used to manipulate specific memory locations, a task necessary for writing low-level operating system components. Similarly, many C++ compilers support an inline assembler. In Java, such code must reside in external libraries, and can only be accessed through the Java Native Interface, with a significant overhead for each call.
Semantics.
C++ allows default values for arguments of a function/method. Java does not. However, method overloading can be used to obtain similar results in Java but generate redundant stub code.
The minimum of code you need to compile for C++ is a function. The minimum for Java is a class.
C++ allows a range of implicit conversions between native types (including some narrowing conversions), and also allows the programmer to define implicit conversions involving user-defined types. In Java, only widening conversions between native types are implicit; other conversions require explicit cast syntax.
A consequence of this is that although loop conditions (if, while and the exit condition in for) in Java and C++ both expect a boolean expression, code such as if(a = 5) will cause a compile error in Java because there is no implicit narrowing conversion from int to boolean. This is handy if the code was a typo for if(a == 5). Yet current C++ compilers usually generate a warning when such an assignment is performed within a conditional expression. Similarly, standalone comparison statements, e.g. a==5;, without a side effect generate a warning.
For passing parameters to functions, C++ supports both pass-by-reference and pass-by-value. In Java, primitive parameters are always passed by value. Class types, interface types, and array types are collectively called reference types in Java and are also always passed by value.
Java built-in types are of a specified size and range defined by the language specification. In C++, a minimal range of values is defined for built-in types, but the exact representation (number of bits) can be mapped to whatever native types are preferred on a given platform.
For instance, Java characters are 16-bit Unicode characters, and strings are composed of a sequence of such characters. C++ offers both narrow and wide characters, but the actual size of each is platform dependent, as is the character set used. Strings can be formed from either type.
This also implies that C++ compilers can automatically select the most efficient representation for the target platform (i.e., 64bit integers for a 64bit platform), while the representation is fixed in Java, meaning the values can either be stored in the less-efficient size, or must pad the remaining bits and add code to emulate the reduced-width behavior.
The rounding and precision of floating point values and operations in C++ is implementation-defined (although only very exotic or old platforms depart from the IEEE 754 standard). Java provides an optional strict floating-point model that guarantees more consistent results across platforms, though possibly at the cost of slower run-time performance, however, Java does not provide strict compliance to the IEEE 754 standard. Most C++ compilers will, by default, partially comply to IEEE 754 standard (usually excluding strict rounding rules and raise exceptions on NaN results), but provide options for stricter compliance as well as less strict compliance (to allow for some optimizations). If we label those options from least compliant to most compliant as fast, consistent (Java's strictfp), near-IEEE, and strict-IEEE, we can say that most C++ implementations default to near-IEEE, with options to switch to fast or strict-IEEE, while Java defaults to fast with an option to switch to consistent.
In C++, pointers can be manipulated directly as memory address values. Java references are pointers to objects. Java references do not allow direct access to memory addresses or allow memory addresses to be manipulated with pointer arithmetic. In C++ one can construct pointers to pointers, pointers to ints and doubles, and pointers to arbitrary memory locations. Java references only access objects, never primitives, other references, or arbitrary memory locations.
In C++, pointers can point to functions or member functions (function pointers). The equivalent mechanism in Java uses object or interface references.
Through the use of stack-allocated objects, C++ supports scoped resource management, a technique used to automatically manage memory and other system resources that supports deterministic object destruction. While scoped resource management in C++ cannot be guaranteed (even objects with proper destructors can be allocated using new and left undeleted) it provides an effective means of resource management. Shared resources can be managed using shared_ptr, along with weak_ptr to break cyclic references. Java supports automatic memory management using garbage collection which can free unreachable objects even in the presence of cyclic references, but other system resources (files, streams, windows, communication ports, threads, etc.) must be explicitly released because garbage collection is not guaranteed to occur immediately after the last object reference is abandoned.
C++ features user-defined operator overloading. Operator overloading allows for user-defined types to support operators (arithmetic, comparisons, etc.) like primitive types via user-defined implementations for these operators. It is generally recommended to preserve the semantics of the operators. Java does not support any form of operator overloading (although its library uses the addition operator for string concatenation).
Java features standard API support for reflection and dynamic loading of arbitrary new code.
C++ supports static and dynamic linking of binaries.
Java has generics, whose main purpose is to provide type-safe containers. C++ has compile-time templates, which provide more extensive support for generic programming and metaprogramming. Java has annotations, which allow adding arbitrary custom metadata to classes and metaprogramming via an annotation processing tool.
Both Java and C++ distinguish between native types (these are also known as "fundamental" or "built-in" types) and user-defined types (these are also known as "compound" types). In Java, native types have value semantics only, and compound types have reference semantics only. In C++ all types have value semantics, but a reference can be created to any type, which will allow the object to be manipulated via reference semantics.
C++ supports multiple inheritance of arbitrary classes. In Java a class can derive from only one class, but a class can implement multiple interfaces (in other words, it supports multiple inheritance of types, but only single inheritance of implementation).
Java explicitly distinguishes between interfaces and classes. In C++, multiple inheritance and pure virtual functions make it possible to define classes that function almost like Java interfaces do, with a few small differences.
Java has both language and standard library support for multi-threading. The synchronized keyword in Java provides simple and secure mutex locks to support multi-threaded applications. Java also provides robust and complex libraries for more advanced multi-threading synchronization. Only as of C++11 is there a defined memory model for multi-threading in C++, as well as library support for creating threads and for many synchronization primitives. There are also many third-party libraries for this purpose.
C++ member functions can be declared as virtual functions, which means the method to be called is determined by the run-time type of the object (a.k.a. dynamic dispatching). By default, methods in C++ are not virtual (i.e., opt-in virtual). In Java, methods are virtual by default, but can be made non-virtual by using the final keyword (i.e., opt-out virtual).
C++ enumerations are primitive types and support implicit conversion to integer types (but not from integer types). Java enumerations can be public static enum{enumName1,enumName2} and are used like classes. Another way is to make another class that extends java.lang.Enum<E>) and may therefore define constructors, fields, and methods as any other class. As of C++11, C++ also supports strongly-typed enumerations which provide more type-safety and explicit specification of the storage type.
Unary operators '++' and '--': in C++ "The operand shall be a modifiable l value. The result is the updated operand; it is an lvalue...", but in Java "the binary numeric promotion mentioned above may include unboxing conversion and value set conversion. If necessary, value set conversion {and/or boxing conversion} is applied to the sum prior to its being stored in the variable.", i.e. in Java, after the initialization "Integer i=2;", "++i;" changes the reference i by assigning new object, while in C++ the object is still the same.
Resource Management.
Java offers automatic garbage collection, which may be bypassed in specific circumstances via the Real time Java specification. Memory management in C++ is usually done through constructors, destructors, and smart pointers. The C++ standard permits garbage collection, but does not require it; garbage collection is rarely used in practice.
C++ can allocate arbitrary blocks of memory. Java only allocates memory through object instantiation. Arbitrary memory blocks may be allocated in Java as an array of bytes.
Java and C++ use different idioms for resource management. Java relies mainly on garbage collection, which can reclaim memory, while C++ relies mainly on the RAII (Resource Acquisition Is Initialization) idiom. This is reflected in several differences between the two languages:
In C++ it is common to allocate objects of compound types as local stack-bound variables which are destroyed when they go out of scope. In Java compound types are always allocated on the heap and collected by the garbage collector (except in virtual machines that use escape analysis to convert heap allocations to stack allocations).
C++ has destructors, while Java has finalizers. Both are invoked prior to an object's deallocation, but they differ significantly. A C++ object's destructor must be implicitly (in the case of stack-bound variables) or explicitly invoked to deallocate the object. The destructor executes synchronously just prior to the point in the program at which the object is deallocated. Synchronous, coordinated uninitialization and deallocation in C++ thus satisfy the RAII idiom. In Java, object deallocation is implicitly handled by the garbage collector. A Java object's finalizer is invoked asynchronously some time after it has been accessed for the last time and before it is actually deallocated. Very few objects require finalizers; a finalizer is only required by objects that must guarantee some cleanup of the object state prior to deallocation — typically releasing resources external to the JVM.
With RAII in C++, a single type of resource is typically wrapped inside a small class that allocates the resource upon construction and releases the resource upon destruction, and provide access to the resource in between those points. Any class that contain only such RAII objects do not need to define a destructor since the destructors of the RAII objects are called automatically as an object of this class is destroyed. In Java, safe synchronous deallocation of resources can be performed deterministically using the try/catch/finally construct.
In C++, it is possible to have a dangling pointer—a stale reference to an object that has already been deallocated. Attempting to use a dangling pointer typically results in program failure. In Java, the garbage collector will not destroy a referenced object.
In C++, it is possible to have uninitialized primitive objects. Java enforces default initialization.
In C++, it is possible to have an allocated object to which there is no valid reference. Such an unreachable object cannot be destroyed (deallocated), and results in a memory leak. In contrast, in Java an object will not be deallocated by the garbage collector until it becomes unreachable (by the user program). (Note: weak references are supported, which work with the Java garbage collector to allow for different strengths of reachability.) Garbage collection in Java prevents many memory leaks, but leaks are still possible under some circumstances