1
Java to C++ (for NumCS)
1. Core Language
Felix Friedrich
Malte Schwerhoff
D-INFK 2022
2
This Course
Goal: ease transition from Java to C++
Expects initial knowledge in Java
Tailored to NumCS – not a generic C++ course
Not self-contained: the gist rather than the details
3
This Course
First course iteration
Content and structure not set in stone
Please interact, ask, suggest, and give feedback
4
Who Is Involved?
Dr. Felix Friedrich Dr. Malte Schwerhoff Prof. Dr. Ralf Hiptmair
D-INFK service lecturers D-MATH (CSE) professor
Long-term C++ lecturer Long-term NumCS lecturer
Lecturer first two weeks Main lecturer
(after 2nd week)
5
Good Sources of Information (on C++ and Eigen)
https://cppreference.com
Programming: Principles and Practice Using C++
(Stroustrup; https://u.ethz.ch/2MgzB+)
A Tour of C++
(Stroustrup; https://u.ethz.ch/aeH7d+)
Effective Modern C++
(Scott Meyers; https://u.ethz.ch/m1DvT+)
Eigen (linear algebra library): https://eigen.tuxfamily.org/dox/
6
A Bit of Background on C++
7
Why C++
Important language in computational science,
engineering, high-performance computing
NumCS uses C++ to discuss implementations
of numerical methods
Often combined with Python (not this course):
user-friendly Python interface to a
high-performance C++ implementation
8
What C++ Is Like
C++ has a certain reputation … partly deserved
But modern C++ (≥ C++11, latest standard is C++20)
has come a long way
9
What C++ Is Like
C++ is not exactly beginner-friendly
- Hardware and OS “shine through” — great for experts, hard for beginners
- Many choices — great for experts, hard for beginners
- Explicit choices — explicate decisions, at cost of syntactical convenience
But C++ is also not opinionated. Two examples:
- Java-style OOP with runtime polymorphism;
but also compile-time polymorphism and static meta-
programming
- Value semantics (think Java primitive types);
but also reference semantics (think Java objects)
10
C++ vs. Java – Big-Picture Differences
Compiled to machine code Compiled to intermediate code
Binaries not portable (OS, hardware) - Portable, but requires
Static code optimisations intermediate layer (JVM)
- Runtime code optimisations
Emphasises efficiency Emphasises safety
(runtime, memory)
Extended C Designed from scratch, with
Inherited efficiency emphasis C/C++ lessons-learned in mind
… and some design problems
(Largely) Interoperable with C
11
What Does C++ Code Look Like?
#include <iostream>
#include <limits>
double average(std::istream&);
int main() {
std::cout << "Enter values to average: ";
double avg = average(std::cin);
std::cout << "Average = " << avg << "\n";
}
// Returns the average of numbers read from the given input stream
double average(std::istream& in) {
double value, sum = 0; // Attention: value is not initialised
unsigned count = 0;
while (in >> value) {
++count;
sum += value;
}
return count != 0 ? sum / count : std::numeric_limits<double>::signaling_NaN();
}
12
Java vs. C++:
Some Similarities and Differences
13
Some Similarities between C++ and Java
Core syntax
- Case sensitivity, curly braces, parentheses, semicolon, comments
Operators
- +, *, =, +=, ==, ++, <, &&, ...
- Same syntax, precedence, short-circuiting, mixed types conversion
Fundamental types: int, float, double, char, bool; also void
Control structures: return, if-else, switch, for, while, ....
Code modularity
- Functions, classes
- Namespaces (and modules with C++20)
14
Differences: Fundamental Types in C++
Signed and unsigned integral types
- E.g. int vs. unsigned int (or just unsigned)
Width of bool, char, int, long is
implementation-defined, with lower bounds
- E.g. an int is at least 16 bits wide, a long 32 bits
- Fixed-width types introduced with C++11, e.g. int32_t
https://en.cppreference.com/w/cpp/language/types Size of 'long integer' data type (C++) on
various architectures and OS (intel.com)
https://en.cppreference.com/w/cpp/types/integer
15
The Price of Efficiency and Generality
Background
1. C++ aims to enable efficient programs on any hardware & OS
2. C++ is old (limited language design experience, fewer hardware & OS standards,
significantly fewer computational resources)
Intuitive trade-off: rigidly defined language semantics increase safety, but
complicate efficient hardware & OS support
C++ language standard grants freedom to language implementers
1. Implementation-defined behaviour: behaviour may vary, must be documented
2. Unspecified behaviour: implementation-defined, no need to document
3. Undefined behaviour: if present, program is not required to do anything meaningful
16
Common Sources of Undefined Behaviour
Signed integer over-/underflow
- Java: well-defined
Out-of-bounds array access, e.g. data[data.size()]
- Java: runtime check, exception
- C++: data.at(idx) performs runtime check
Null-pointer dereferencing
- Java: runtime check, exception
Read & write same memory location in single expression, e.g. cout << (x + x++)
- Java: well-defined
Reading uninitialized variables
- Java: compiler check for local variables, default initialisation for member variables
Accessing destructed objects/deallocated memory
- Java: cannot happen, since garbage collector manages memory
17
Differences: Initialisation
C++ default-initialises variables iff the type declares a default constructor
- Applies to local variables, and member variables (object fields)
No default constructors for fundamental types (int, double, ...)
int count; // Not initialised C++ int count; // Not initialised Java
cout << count; // Undefined behaviour println(count); // Compiler error
C++: reading uninitialised variables is undefined behaviour
Java:
- Guaranteed compiler error for local variables
- Default initialisation for member variables
18
Undefined Behaviour
Compile- or runtime checks might be performed, but no guarantees
Keep existence in mind
- To avoid common problems (previous slides)
- During debugging (undefined behaviour often results in nondeterministic behaviour)
Otherwise not central for this course
- We will not go into further details
- Newer C++ versions sometimes replace undefined by unspecified behaviour
If interested: https://en.cppreference.com/w/cpp/language/ub
Demo of what may happen: https://gcc.godbolt.org/z/MsjPnWs5Y
19
Differences: Expression Evaluation
Evaluation order of subexpressions
- Unspecified behaviour in C++
- Specified as left to right in Java +
Compound expression example: e1 + e2 * e3 e1 *
- Java: e1 evaluated first, then e2, finally e3
e2 e3
- C++: Any order allowed
Likewise for function call arguments, e.g. fun(e1, e2, e3)
Relevant (only) when expressions have side-effects
20
Differences: Automatic Numerical Conversions
Automatic conversion to bool
- In C++, variables of fundamental type can be used as bool without explicit casting
Automatic narrowing between numeric types
- In C++, e.g. assigning a double to an int does not require an explicit cast
Automatic numerical promotions in mixed numerical expressions
- E.g. some_short * some_double + some_int → all values promoted to doubles
- Rules analogous to Java’s:
https://en.cppreference.com/w/cpp/language/operator_arithmetic#Conversions
- C++-specific: signed promoted to unsigned → -1 < 1u is false
(https://www.modernescpp.com/index.php/safe-comparisons-of-integrals-with-c-20)
21
Differences: Type Inference
C++ is statically typed, just like Java
Limited support for type inference includes
- Type parameter interference → templates, 2nd week
- Keyword auto
std::vector<int> data = ...; std::vector<int> data = ...;
std::vector<int>::iterator head = data.begin(); vs. auto head = data.begin();
Types serve as code documentation → don’t overuse auto
auto model = extract_model(...); // What’s model? Which functions does it provide?
22
Values vs. References
23
Value Types / Pass by Value
Consider the example on the right void swap(int x, int y) {
int tmp = x;
Can you explain the observed x = y;
behaviour? y = tmp;
}
just as
int main() { in Java
int x = 1;
int y = 2;
swap(x, y);
std::cout << x << ", " << y; // 1, 2
}
24
Value Types / Pass by Value
Consider the extended example. Can you explain the observed behaviour?
struct Coordinate { // Java class void swap(Coordinate c) {
int x; int tmp = c.x;
int y; c.x = c.y;
c.y = tmp;
Coordinate(int a, int b) { }
x = a;
y = b; int main() {
} Coordinate c(1, 2); // Obj. construction
}; swap(c);
std::cout << c.x << ", " << c.y; // 1, 2
}
not as
in Java
25
Value Types / Pass by Value
By default, types in C++ are value types
- Fundamental types (int, etc.): same as in Java
- Instances of classes: different from Java
Value types (of statically-known size) can be allocated on the stack
- Typically faster than heap accesses
26
Outlook: Values vs. Pointers
Java’s references are (a simplified version of) pointers in C++
Coordinate* location = new Coordinate(1, 2);
- The pointer value (memory address) is still passed by value (i.e. copied), as in Java
C++ has no garbage collector → manual memory management
- Every new needs a delete
- Danger of memory leaks, accessing deleted objects, freeing memory twice
- Smart pointers simplify memory management (while preserving efficiency)
Pointers are not relevant for NumCS → not discussed further
27
Pass by Reference / Reference Types
Pass by value not always desirable
1. Effects: modifying a shared object (e.g. a shared counter in a concurrent system)
2. Efficiency: avoiding copying large data (e.g. a huge matrix)
Values can be shared via C++ references
- Still no heap involved (efficiency)
28
Pass by Reference / Reference Types
Pass-by-reference semantics with C++ references
struct Coordinate { void swap(Coordinate& c) {
... // same as before int tmp = c.x;
}; c.x = c.y;
c.y = tmp;
} just as
in Java
int main() {
Coordinate c(1, 2);
swap(c);
std::cout << c.x << ", " << c.y; // 2, 1
}
29
Pass by Reference / Reference Types
References can (nearly) be used everywhere, e.g.
int anakin_skywalker = 7;
int& darth_vader = anakin_skywalker;
darth_vader = 40; // Oh oh, Anakin rapidly aged ...
Reference types are typically used
- As function parameters
- In lambda expressions (2nd week)
- As member variables (classes, this Thursday)
30
Pass by Reference / Reference Types
References must be initialised
int& darth_vader; // Compiler error
References cannot be re-aliased:
int& darth_vader = anakin_skywalker;
darth_vader = kermit_the_frog;
// vader (and thus anakin) gets kermit’s value,
// but vader does not become an alias of kermit
31
Pass by Reference / Reference Types – With Fundamental Types
void swap(int& x, int& y) { Can you do this in Java?
int tmp = x;
x = y;
y = tmp;
}
int main() {
int x = 1;
int y = 2;
swap(x, y);
std::cout << x << ", " << y; // 2, 1
}
32
Constants
Java’s final is const in C++
const unsigned speed_of_light = 299792458;
...
speed_of_light *= 2; // Compiler error
A program is const-correct if all variables not intended to be mutated are
typed as const
Concept lifted to constant expressions (= evaluable at runtime) to support
compile-time meta-programming
- Not further details
- If interested: https://en.cppreference.com/w/cpp/language/constant_expression
33
Const-References
Consider the code below. Can it be improved?
bool is_sorted(std::vector<int> v) {
for (unsigned i = 1; i < v.size(); ++i) {
if (v[i] < v[i-1])
return false;
}
return true;
}
std::vector<int> v(10'000'000); // Size 10e7
...
if (is_sorted(v)) ...
34
Const-References
Yes: const-reference for efficiency (no copy) and safety (no mutation)
bool is_sorted(const std::vector<int>& v) {
// ... as before ...
}
std::vector<int> v(10'000'000);
...
if (is_sorted(v)) ...
35
Functions & Operators
36
Functions
Core syntax as in Java: R name(T1 p1, …, Tn pn) { body }
Function overloading Default parameters
void process(int data) { ... } // Returns true if data contains elem
void process(double data) { ... } // at least rep times
bool occurs(
int x = ...; int elem,
std::vector<int> data,
process(x); // calls 1st function unsigned rep = 1) {
process(3.13); // calls 2nd function
...
}
std::cout << occurs(0, my_data);
std::cout << occurs(77, my_data, 2);
37
Functions
Variadic functions
double average(double values...) {
// compute average of given values
}
double avg1 = average(2.5, 1.7);
double avg2 = average(avg1, 13, 7.4, 28.9);
Declaration very similar to Java – looks nice …
… but usage inside the function does not, thus omitted
https://en.cppreference.com/w/cpp/utility/variadic
Alternative: variadic templates (2nd week)
38
Operator Overloading
In contrast to Java, C++ supports overloading of operators
struct Rational { Rational r1(1, 3);
int n; int d; Rational r2(5, 7);
Rational(int _n, int _d) { n = _n; d = _d; }
std::cout << (r1 + r2);
};
Rational operator+(const Rational& l, const Rational& r) {
return Rational(l.n * r.d + l.d * r.n, l.d * r.d);
}
std::ostream& operator<<(std::ostream out&, const Rational& r) {
out << r.n << ", " << r.d;
return out; // enables chaining of <<
}
https://en.cppreference.com/w/cpp/language/operators
39
Separate Compilation
40
Semantics of #include
main.cpp main.cpp (as compiled)
#include <file.cpp>
#include <rational.cpp> struct Rational {
int n; is replaced with the
int main() { compile int d; content of file.cpp
Rational r1 = ...;
... main.cpp Rational(int a, int b) {
}
n = a;
d = b;
Possible
rational.cpp } disadvantages?
};
struct Rational {
int n; ...
int d;
int main() {
Rational(int a, int b) { Rational r1 = ...;
n = a; ...
d = b; }
}
};
...
41
Includes and Separate Compilation
Fact: #include <lib.cpp> is replaced with the contents of file lib.cpp
Goal: enable separate compilation of units of code (e.g. classes, libraries);
resulting machine code then linked afterwards
- Improves performance (shared library only compiled once)
- Allows binary distributions (e.g. closed-source libraries)
Question: Given how #include works, how to enable separate compilation?
C++ solution: separation of declarations from definitions in separate files
- Header files .h contain declarations, implementations resides in .cpp files
42
Header vs. Implementation
main.cpp rat.h rat.cpp
#include <rat.h> struct Rat { Rat::Rat(int a, int b) {
int n; n = a;
int main() { int d; d = b;
Rat r1 = ...; }
... Rat(int a, int b); };
} };
Rat operator+(const Rat& l, const Rat& r) {
Rat operator+( return Rat(l.n * r.d + l.d * r.n, l.d * r.d);
const Rat& l,
Declarations suffice const Rat& r);
}
for compilation // more declarations ...
// more implementations ...
rat.cpp and main.cpp can be compiled separately
$ g++ -c main.cpp (produces main.o)
$ g++ -c rat.cpp (produces rat.o)
… and linked together later
$ g++ rat.o main.o –o main (produces executable main)
43
Preventing Re-inclusion
If rat.h is (transitively) included multiple times, symbols are redeclared
Avoid by using pre-processor Alternative using preprocessor
macros (not discussed further) directive (not discussed further)
to establish include guards: - Certain advantages over include guards
rat.h - Not supported by all compilers
#ifndef RAT_H
rat.h
#define RAT_H
#pragma once
struct Rat {
int n; struct Rat {
int d; int n;
int d;
Rat(int a, int b);
}; Rat(int a, int b);
};
// more declarations ...
// more declarations ...
#endif // RAT.H
44
Separate Compilation on Code Expert
Code Expert environments typically don’t give you access to the compiler
command-line
- Advantage: simpler to use, less potential for mistakes
- Disadvantage: separate compilation might not be possible
Probably only relevant if you create your own files
(in contrast to completing given skeleton files)