The C++ programming language is far more complex than the Java language. Due to this, C++ coding is more error prone than Java code. This has impacts on maintainability1 and feasibility of large projects (within limited resources2).
Being more complex and less restricted, C++ typically needs policies and coding rules, whereas Java does so in a much minor extend.
Due to being optionally less dynamic, C++ is more optimizable (assuming the now usual modus of compiling each Java class on its own (to follow the model of loading each class on its own), i.e. omitting global analysis and global optimization). Some of C++'s optimizability arises from omitted method calls and dispatching, some optimizations are possible by knowing more code context; optimizability affects performance and space requirements.
C++ allows more control over compile-time optimization, whereas Java depends on the specific runtime optimization used, e.g. with just-in-time optimizing virtual machines. Just-in-time compilers may be able to consider the details of CPU instruction sets (at runtime) and therefore translate to more efficient native code. In C++ such optimizations are often done by providing many platform-specific dynamic libraries (the decision which to load/use done at runtime); however code layout has to be planned to isolate such parts and compile with a set of different platform-specifying options.
Dynamic compilers might be better suited to consider runtime statistics, to rearrange code accordingly (similar to [lower level] branch prediction).
Optimization generally and inlining specially make it harder to reverse-engineer binaries; so implementation secrets are stronger protected in C++ than in Java.
These [language] safety issues affect [program] reliability (and the cost to make software work). C++ programs crash where Java issues useful diagnostic information (stack traces of causing and detecting code). Many of the aspects make problem analysis harder in C++, due to deferred problem detection. Undetected error conditions, especially with [user provided] data input, cause security problems, and vulnerabilities.
Besides the language safety aspects, Java [typically] offers a sandbox model of runtime program execution: certain code may be restricted from using certain parts of the APIs. The model however adds complexity (if used explicitly by the [application] programmer). C++'s model makes the prevention of [arbitrary/unallowed] memory accesses infeasible; C++'s security typically is implemented by means of crossing process/system boundaries ([Solaris] door calls, or system call interfaces).
Java implements metadata, to support observing reflection (and introspection).
Java incorporates some design decisions that make the language simpler or more comfortable to use.
Java provides portability to other [computer] platforms, and to other programming languages.
less memory models
- Java allocates all its objects on the heap (all managed by the garbage collector); C++ additionally offers stack based instantiation (including temporary objects), and static instantiation. Stack objects are automatically deleted (when they go out of scope),
so a programmer has to assure no references are kept further (e.g. in a container, registry, etc.). Instantiation order with static instantiation is difficult to predict, and therefore error-prone, less portable, and restricted. no temporary objects
- [Based on non-trivial rules] C++ language will chose either a cast or temporary object (including object construction) to [implicitly] coerce one type into another, sometimes leading to unwanted effects or unwanted overhead.
no copy constructor and assignment operator needed
- Due to Java's memory model, no copy constructor and assignment operator are needed, and therefore can't become a source of bugs.
no explicit cast code supported
- C++ increases the number of routes for type coercion by allowing to defined explicit cast code.
no ambiguity between int, boolean, and null pointer
- Due to
false
andnull
both being distinct from0
there's no ambiguity if used e.g. as parameters in overloaded methods3. no default parameters
- As not supported, there can't be an ambiguity between methods with default parameters and other overloaded methods.
no unsigned integers
- As unsigned integers don't really buy anything (in C/C++ neither, decent casts can be used), they're omitted.
const not supported
- Object immutability is in Java at most considered via annotation4. The more intuitive
final
modifier allows to implement first-level constness on variables and fields (again being more intuitive than C++'s reference fields). expression evaluation order
- Evaluation order within expression is stricter defined in Java, leading to more deterministic behavior, and avoiding a specific source of problems. C++ compiler however may allow options to warn about undefined semantics outside of sequence points.
no macro pitfalls
- Macros offer call-by-name semantics and therefore allow unwanted side effects, e.g. with multiple evaluation (
max(i++,j++)
), or precedence issues (with missing parentheses).5
Ease of use
no splitting between declaration and definition
- As Java doesn't split the interface and its implementation (in C++ into header files and implementation files), there's less overhead with the duplication of declarations6.
critical section handling built-in
- Support for critical section handling, typically needed with multithreaded code, is closely built into the Java language; methods synchronize on their [
this
] object simply by specifying thesynchronized
modifier; regions of code use asynchronized
block; both are exception-safe.7 garbage collection
- Java's garbage collection largely avoids the need for manual reference house-keeping8. (
finalizers
andjava.lang.ref.SoftReferences
allow some useful interaction with the garbage collector (however their use increases code complexity).) Unreferenced/unreachable objects are not occurring in Java. Garbage-collected environments allow (immutable) copy-on-write data structures, which are useful for lock-free (fast) implementations of concurrent algorithms. no destructor code needed
- Destructor code is not needed in Java, mainly due to its garbage collection memory model. Garbage collection prevents the types of memory leaks found in C++.
keeping backward compatibility
- If a class [during code changes] grows in data size, the using C++ code has to be recompiled, since it's the instantiator's task to allocate memory (and thus needs knowledge about size), which is not the case in Java; it's easier to keep compatibility in Java.
no cross-compilation needed
- Since the compiled bytecode is hardware-platform independent, there's no need for cross-compilation or having access to different platforms.
Code readability
additional keywords
- Java incorporates more keywords where appropriate, to enhance readability:
abstract, extends, implements, interface, super, null.
additional types
- Java explicitly distinguishes between integers and boolean, as well as between bytes and characters:
boolean, byte,
and defines the literalsfalse, true.
Need for rules
Being more complex and less restricted, C++ typically needs policies and coding rules, whereas Java does so in a much minor extend.
Code optimizability
Due to being optionally less dynamic, C++ is more optimizable (assuming the now usual modus of compiling each Java class on its own (to follow the model of loading each class on its own), i.e. omitting global analysis and global optimization). Some of C++'s optimizability arises from omitted method calls and dispatching, some optimizations are possible by knowing more code context; optimizability affects performance and space requirements.
C++ allows more control over compile-time optimization, whereas Java depends on the specific runtime optimization used, e.g. with just-in-time optimizing virtual machines. Just-in-time compilers may be able to consider the details of CPU instruction sets (at runtime) and therefore translate to more efficient native code. In C++ such optimizations are often done by providing many platform-specific dynamic libraries (the decision which to load/use done at runtime); however code layout has to be planned to isolate such parts and compile with a set of different platform-specifying options.
Dynamic compilers might be better suited to consider runtime statistics, to rearrange code accordingly (similar to [lower level] branch prediction).
no inlining or macros
- Inlining code allows to consider it in a specific context, to optimize specifically, over more code (without generally breaking [object-oriented] encapsulation).
no template instantiation
- Template instantiation generates type specific code that is more optimizable than its generic counterpart (that merely does type checks only).
no non-virtualization
- Allows to omit dispatch calls and to inline specific code (and hence to optimize over more code).
no on-stack instantiation
- Allows to omit memory management. Just-in-time compilers may consider on-stack instantiation though.
no composite object
- Unlike with Java, C++ allows to directly compose objects, which avoids indirection (via references), and makes exact types known (possibly further avoiding method dispatch indirection).
Reverse-engineering
Optimization generally and inlining specially make it harder to reverse-engineer binaries; so implementation secrets are stronger protected in C++ than in Java.
Safety
These [language] safety issues affect [program] reliability (and the cost to make software work). C++ programs crash where Java issues useful diagnostic information (stack traces of causing and detecting code). Many of the aspects make problem analysis harder in C++, due to deferred problem detection. Undetected error conditions, especially with [user provided] data input, cause security problems, and vulnerabilities.
no uninitialized pointers and data
- Java implicitly initializes data fields [if not done explicitly [by the programmer], to useful defaults (null, false, 0, ...)], and forces explicit initialization on local variables. Though C++ compilers may take care on this too9, either by initializing10, or issuing warnings.
no dangling/stale pointers
- Java's garbage collection prevents dangling references.
array bounds checks
- Boundary violation with arrays are checked at runtime, leading to an
IndexOutOfBoundsException
in error case. Two issues make this detail very important: [stack] data overwrites [with 'garbage'] in C++ get noticed [much] later, making problem analysis [more] difficult; and [stack] buffer overwrites are a major source of security problems. null pointer handling
- In Java, dereferencing a null pointer generates a
NullPointerException
, which may be reasonably caught. As with [all] Java exceptions, a stack trace is included, to help identifying culprit code locations, and enhancing error diagnostics. C++ code often just crashes dereferencing a null pointer11. type cast checks
- Java only allows casts between object types if permitted by class hierarchy relationship, i.e. with objects only the equivalent of C++'s
dynamic_cast
is supported. unchecked error conditions avoided through exceptions
- Since in Java error conditions are [more consequently] propagated through exceptions, they cannot be easily ignored12. Ignoring error conditions at their place of appearance can cause [more] harm to the program's internal state and, as problems are reacted on only later, makes [deferred] problem analysis more difficult.
explicit boolean type
- In Java
boolean
is an explicit type, different fromint
. Also the constantstrue
andfalse
are provided. This feature disables integer assignments in conditionals (though [optionally] also checked with C++ compilers13). link vs. runtime compatibility
- Class [linking vs. runtime] incompatibilities are detected at their origin, and can even be caught (and handled) at runtime. C++ issues 'unpredictable' behavior (e.g. in the case of a shorter or reorganized method dispatch table).
memory model strictly defined
- Java's memory model is defined to cover all multithreading and optimization issues.
Diagnostics
stack trace with exceptions
- Java exceptions include a stack trace, to help identifying originating buggy code locations; the given [call stack] context often provides hints for problem solution/avoidance.
linked exception chain
- If a lower level exception triggers an upper level exception (since it's caught and transformed to an application-level exception), the originating exception is linked, thus providing information needed for analysis of all software layers.
thread dumps
- Java runtime environments provide thread dumps with stack traces of all threads, to allow analysis of such things as deadlock conditions or starvations; these thread dumps include information about monitors held or waited for.
VM runtime information
- Java runtime environments further may provide information about memory use, garbage collection, locks. C++ runtime environments are free to offer similar, however likely not provided in many environments.
Sandbox security model
Besides the language safety aspects, Java [typically] offers a sandbox model of runtime program execution: certain code may be restricted from using certain parts of the APIs. The model however adds complexity (if used explicitly by the [application] programmer). C++'s model makes the prevention of [arbitrary/unallowed] memory accesses infeasible; C++'s security typically is implemented by means of crossing process/system boundaries ([Solaris] door calls, or system call interfaces).
fine-granular permissions
- Permissions for operations can be specified in a sufficiently fine-granular way, and are enforced by the runtime environment. E.g. [web] applets may not be allowed to read/write [local] files. Code often is not allowed to change the classloader (to not compromise the rest of the security).
privileged namespaces
- Java offers the treatment of 'trusted' [privileged] package namespaces. These are enforced by the classloader.
restricted polymorphism
- 'Trusted' types may be confined (from deriving/overloading) by
final
modifier on classes and methods (preventing further polymorphism), to make references on the type predictable/safe in use (e.g.java.lang.String
). byte code verifier
- The class loaders typically verify byte code for violations that may affect security or would lead to VM crashes.
Metadata
Java implements metadata, to support observing reflection (and introspection).
dynamic code invocation
- Dynamic invocation allows to dynamically bind code into frameworks, e.g. application containers. This allows for deployment of code with less overhead than e.g. with RMI or CORBA.
inspectable objects
- Inspectable data objects yield simple data interfaces. Samples are Java beans. Default serialization uses this feature too.
array length
- The length of an array object is accessible through
length
, making 'end-element' policies needless14, and allowing for more safety (bounds violations can be checked). annotations
- With annotations, Java supports structured meta data, e.g. meta information that helps ensure policies. C++ may do some (but not all) of the policies viapragmas.
Design considerations
Java incorporates some design decisions that make the language simpler or more comfortable to use.
single inheritance
- Strict single inheritance is enforced in Java, which makes the design clearer. Instead of multiple inheritance the more controlled
interface
construct (pure abstract class) is supported. single-rooted classes
- All classes are single-rooted by the class
Object
. This enables such things as the use of mixed-type containers. useful root class methods
- Methods, that are needed for the [mixed-type or not] containers, (i.e. that are of importance to building data structures), are predefined [for each class]:
equals, hashCode.
Likewise, string representation is [usually] done via a methodtoString.
no global data
- Global data (outside class blocks) is not supported. This makes the interfaces less complex. However, static class data in (possibly uninstantiable) classes is supported.
no operator overloading
- Overloaded operators, which may be hard or unintuitive to read, are not supported in Java.
no goto
goto
is not supported. However, multilevelbreak
andcontinue
is supported.multilevel break and continue
- The control flow in nested loops is enhanced with multilevel
break
andcontinue
. no reference parameters
- Java lacks reference parameters, establishing the need for holder objects, returning object arrays, or introducing helper objects, or keeping objects mutable.
nested classes and inner classes
- Java allows to further partition namespace and enhance encapsulation by offering
private static
[nested]classes
, and eases accesses of outer context by non-static
[inner]classes
. in-source documentation
- In-source code documentation comments are provided. Documentation keywords are supported as well:
@author, @version, @since, @param, @see
, etc. It's however easy to use extractable documentation comments in C++ too.
Portability
Java provides portability to other [computer] platforms, and to other programming languages.
sizes of the integer types defined
- The sizes of the integer types
byte, short, int
andlong
are defined to be 1, 2, 4 and 8 bytes. The choice of integer type doesn't need platform considerations. byte order defined
- The well defined network byte order is used to write primitive data types to file or network15.
no alignment and padding
- As [native] memory can't be directly written to file or network, no portability problems with different alignments or paddings arise.
default serialization
- Java provides [however inefficient16] default serialization for [composed] data types (objects, classes), also considering version compatibility issues.
container classes
- Serializable container classes make it easy to ship complex data across platforms.
unicode provided
- Unicode is used as the character type and as base for strings. It supports a large set of scripts.
No comments:
Post a Comment