Java vs C++
Memory Management
- Java: uses virtual machines (JVM) which has built-in garbage collection (managed runtime).
- you freely create new objects, JVM will clean up the objects when they are no longer used.
- you do not need to know where the objects are located in memory; and JVM may move the objects around due to garbage collection.
- C++: no garbage collection.
- you have to manually track the usage of the objects and free the memory.
- you can directly work with memory locations: once an object is allocated in moemory it does not move. You can find the location using address-of operator
&
, like&x
. - you need to understand and work with pointers.
References and Pointers
C++ references behave differently than Java references.
- Java: references behave similarly to C++ pointers; for example, you can change the object that a Java reference refers to, and you can assign
null
to a Java reference. - C++: think of a C++ reference as an alias — another name for an existing object. Unlike pointers, references cannot (legally) be
null
; they always refer to something. (a reference referring tonull
is one type ofUndefined Behavior
).
Stack vs Heap
- stack: stack pointer move down to create new memory, move up to release. efficient.
- Variables within the function are allocated on the stack — created within a stack frame upon entering the function and destroyed when destroying that stack frame upon exiting the function.
- heap: unlike the stack, the compiler does not need to know how long the storage must stay on the heap; dynamically create and release; takes longer time than stack.
- for an object needs to outlive the scope where it was created, for example by a factory function.
Differences in Java and C++:
- Java:
- primitive on stack:
boolean
,char
,byte
,short
,int
,long
,float
,double
,void
; more efficient. - all objects are created on the heap, using
new
. - object references are on the stack.
A a;
meansa
is just a reference, nothing is created yet;A a = new A();
will actually create an object on heap.- both stack and heap in Java are managed by JVM, will be auto cleaned up.
- primitive on stack:
- C++:
A a;
will allocate memory in stack;A* a = new A();
orauto a = std::make_unique<A>();
will allocate memory on the heap, anda
is a pointer to that memory. Raw pointers needs manual calls todelete
; smart pointers will free the memory automatically.- can also be created on the heap using C apis like
malloc()
. - stack memory will be auto-released as soon as the variable is out of scope, but not for heap (so if they are not freed there's a memory leak).
Objects
- Java:
int i
is not an object,Integer i
is. - C++:
int i
is an object (int
itself is atype
, which is not an object).
Memory Overhead
- Java:
- 32-bit Java: 8-byte overhead per object.
- 64-bit Java: 16-byte overhead per object.
- C++:
- no garbage collection and cannot use arbitrary objects for synchronization.
- If you have classes with overridden methods, each object has a pointer to its vtable, just like the Java object's reference to its class.
std::unique_ptr
has memory overhead only if you provide it with some non-trivial deleter.std::shared_ptr
has overhead because of it's memory allocation for the control block (which keeps the ref counter and a pointer list to all weak references);std::shared_ptr
is always a 2 pointer tuple (one to the object, one to the control block).
Early Binding vs Late Binding
- Java: Early binding, know the type during compiling.
- C++: Late binding, do not know the exact type during compiling, but know in run time. Use
virtual
.
Reference Semantics vs Value Semantics
- Java: variables have reference semantics.
- C++: variables have value semantics.
Reference Semantics
When you assign an object variable to another variable in Java, you end up with two variables that refer to the same object.
// Java
String a = new String("Foo");
String b = a;
String c = new String("Foo");
assert(a == b); // a and b refer to the same object
assert(a != c); // a and c refer to different objects
assert(a.equals(c)); // a happens to have the same value as c
You can do the same thing in C++, using pointer syntax or reference syntax:
// C++
std::string a = "Foo";
std::string& r = a;
std::string* p = &a;
std::string b = "Foo";
assert(&a == &r); // a and r both refer to an object at the same address
assert(&a == p); // a and p both refer to an object at the same address
assert(&a != &b); // a and b refer to objects at different addresses
assert(a == b); // a happens to have the same value as b
Value Semantics
In Java, when you assign one primitive variable (e.g. int
, float
, boolean
, etc) to another (int a = b
), rather than making a new reference, Java simply copies the value of the first variable into the second. Such copying value semantics are what C++ uses for all variables! C++ uses a type's copy constructor or assignment operator (operator=
) to assign one value to another, and if you don't provide these methods for your type, C++ will provide a default for each.
Here's an example to help show this fundamental difference between Java and C++:
// Java
Set<String> s1 = new TreeSet<>();
Set<String> s2 = s1;
s1.add("Alice");
assert(s2.contains("Alice")); // True, because s1 and s2 refer to the same Set.
// C++
std::set<std::string> s1;
std::set<std::string> s2 = s1;
s1.insert("Alice");
assert(s2.empty()); // True, because s2 was copied from s1 at construction time.
Pass By Value vs Pass By Reference
- Java: parameters (including object's reference) are always passed by value.
- C++: parameters could be passed by value, pointer or reference.
Type
Unsigned types
- Java does not support unsigned arithmetic.
- C++ does.
Byte
- C/C++:
byte
ranges from0
to255
. - Java:
byte
is signed:-127
to128
.
Compare Strings
- C++:
string::operator==()
is usingstring::compare()
under the hood, so the 2 strings can be compared directly using==
. - Java:
==
checks if two strings are pointing to the same string, but to compare string content, useequlas()
.
Classes
Constructors
Constructors in C++ and Java behave very similarly.
- Types:
- Java: constructor.
- C++: constructor, copy constructor, move constructor.
- Default constructor: In both languages, the compiler provides a default constructor if you don't provide one. A default constructor simply creates an "empty" instance of an object.
- Constructor Ordering:
- The order C++ calls constructors is similar to the order Java would call them.
- C++ has a bigger difference between initialization and assignment than Java does — assignment is disallowed for some types (such as const types).
- Unlike Java, the order of construction within a function argument list or other subexpression is unspecified in most cases. Given
run(Foo(a), Bar(b))
you can't assumeFoo
will be constructed beforeBar
! - The rules for variables at namespace scope are complex. Notably, there is no relative ordering between different translation units! By contrast, Java has a well-defined order for initializing static variables, so this difference forms an area of C++ with subtle and potentially significant pitfalls. This is one of the reasons Google Style prohibits non-trivial global variables.
Destructor
- C++: destructors run within the program's flow of control, guaranteed to run.
- The general rule is that destructors are called in the reverse order that the related constructors were called.
- Java: does not have destructors; the closest concept is the
finalize()
method, it is triggered during garbage collection, outside of program's flow of constrol, may or may not be called.finalize()
is deprecated in JDK 18.
Inheritance
- Java: single rooted hierarchy, inherit from
Object
, does not support multiple inheritance. - C++: supports multiple inheritance.
Overloading
- Java: does NOT support operator overloading, only method overloading.
- C++: supports both method overloading and operator overloading.
Portability
- C++: platform dependent, the source must be recompiled for different platforms.
- Java: platform independent, Java code will be compiled to bytecode, which will be used by JVMs. No need to recompile since JVM will handle differences among different platforms.
Functions
- C++: Functions and data may exist external to any class, global and namespace scopes are available.
- Java: All function and data exist within classes; package scope are available.
Return Code
- C++: The integer returned from the
main
function is the process exit status, indicating if the code is successful. - Java: Not the return code from
main
, insteadSystem.exit(int status)
has to be called.
public static void main(String[] args) {
System.exit(1);
}
Actually, Java's main
function has to return void
, otherwise an error will be thrown:
Error: Main method must return a value of type void in class XXX, please define the main method as:
public static void main(String[] args)
Interacting with native systems
- C++: can direct call native system libraries.
- Java: only through JNI (Java Native Interface) or JNA (Java Native Access) or the new Foreign-Memory Access API / Foreign Linker API.
Filenames
- Java: class name must match filename, e.g
class A
must be stored inA.java
. - C++: no such restriction.
C++ in JVM
OpenJDK is implemented in C++ and Java.
From JDK 16, C++14 features can be used in JVM.
Exceptions
Run-time checking: C++ exception specifications are checked at runtime rather than at compile time, so they offer no programmer guarantees that all exceptions have been handled.
Volatile
- Java:
volatile
tells the compiler that the value of a variable must never be cached as its value may change outside of the scope of the program itself (by another thread). Used for inter-thread communication. - C/C++:
volatile
is needed when developing embedded systems or device drivers, where you need to read or write a memory-mapped hardware device. Only meant for use for hardware access; do not use it for inter-thread communication.