diff options
-rw-r--r-- | src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/cs0104_lesson_01.cpp | 1319 |
2 files changed, 1320 insertions, 1 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index de97e60..ab8da60 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,7 +3,7 @@ include_directories( ) # These examples use the standard separate compilation -file(GLOB_RECURSE SOURCES_IDIOMATIC_EXAMPLES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "lesson*.cpp" "example*.cpp") +file(GLOB_RECURSE SOURCES_IDIOMATIC_EXAMPLES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "lesson*.cpp" "cs*lesson*.cpp" "example*.cpp") string( REPLACE ".cpp" "" BASENAMES_IDIOMATIC_EXAMPLES "${SOURCES_IDIOMATIC_EXAMPLES}" ) diff --git a/src/cs0104_lesson_01.cpp b/src/cs0104_lesson_01.cpp new file mode 100644 index 0000000..57a0065 --- /dev/null +++ b/src/cs0104_lesson_01.cpp @@ -0,0 +1,1319 @@ +//============================================================================ +// Author : Sven Gothel +// Copyright : 2020-2024 Gothel Software e.K. +// License : MIT +// Description : CS 01.04 C++ 01 +//============================================================================ + +#include <cassert> +#include <cstdint> +#include <cmath> +#include <stdexcept> +#include <limits> + +#include <iostream> +#include <sstream> + +/** + * Expressions used in Statements to initialize variables of primitive type. + * - Variablen mit primitiven Datentypen + * - Literale (Konstanten) + * - Operatoren `=`, `+` fuer `int` Werte + * - Testen der Variablen Inhalte + */ +static void test01() { + /** + * ℤ Ganzzahlen -> Integer Variable, primitiver datentyp `int` + * - Hier mit Variablenname `i` + * - Hier besetzt die `int` Variable 32-bit oder 4 Byte Speicher + * + * Genutzte Literale (Konstante) + * - `1` Wert 1 als `int` + * - `2` Wert 2 als `int` + * + * Genutzte Operatoren + * - `=` wert-zuweisung, assignment + * - `+` addition + */ + int i = 1; // ℤ ganzzahl, variable (veraenderbar, mutable), initialisiert mit dem Wert `1` (literal) + i = i + 1; // Erhoehe den Inhalt der Variable `i` um `1` (literal). + assert(2 == i); // Test das variable `i` den Inhalt `2` (literal) hat. + + /** + * ℤ Ganzzahlen -> Integer Variable, primitiver datentyp `int` + * - Hier mit Variablenname `j` + * - ... + */ + int j; // Undefiniert, nicht initialisiert. + j = 1; // Weise der Variable den Wert `1` (literal) zu. + j = j + 1; // Erhoehe den Wert der Variable `j` um `1` (literal) + assert(2 == j); // Test das variable `j` den Inhalt `2` (literal) hat. +} + +/** + * Block-Statements und Lebensbereich lokaler Variablen + */ +static void test02_3_block_statement() { // Block-0 Anfang + // Block-1 Anfang + { + const int i = 1; // Neue variable `i` mit dem Wert `1` initialisiert + assert(1 == i); + + // Block-2 Anfang + { + const int j = 2; // Neue variable `j` mit dem Wert `2` initialisiert + const int k = i + j; // Neue variable `k` mit `i+j` initialisiert + assert(3 == k); + } + // Block-2 Ende + // Variablen `j` und `k` existieren nicht mehr! + } + // Block-1 Ende + // Variable `i` existiert nicht mehr! + // i = i + 1; FEHLER, `i` gibts nicht mehr + + // Neue verschachtelte Bloecke mit neuen Variablen & Lebensbereich + { + const int i = 2; + { + const int j = 3; + const int k = i + j; + assert(5 == k); + } + } +} // Block-0 Ende + +/** + * Primitive Datentypen und Literale (Konstante) + */ +static void test03_1_1_literals() { + const bool b0 = false; // boolean-value literal + const uint8_t o0 = 0b0010; // bit-value literal -> decimal 2 + const char c0 = 'C'; // UTF-16 character value literal + const char c1 = '0'; // UTF-16 character value literal + const short s0 = 0x0A; // hex-value literal -> decimal 10 + const int i0 = 100; // int-value literal (default) + const long l0 = 1000L; // long-value literal + const float f0 = 3.14f; // float-value literal + const double d0 = 2.14; // double--value literal (default) + assert(false == b0); + assert((uint8_t)2 == o0); + assert('C' == c0); + assert(0x30 == c1); + assert(10 == s0); + assert(100 == i0); + assert(1000 == l0); + assert(std::abs(3.14f - f0) <= std::numeric_limits<float>::epsilon()); // Test value mit erlaubter Fehler-Toleranz std::numeric_limits<float>::epsilon() + assert(std::abs(2.14 - d0) <= std::numeric_limits<double>::epsilon()); // Test value mit erlaubter Fehler-Toleranz std::numeric_limits<double>::epsilon() +} + +/** + * Binary-Operatoren der 4 Grundrechenarten und Modulo (Divisionsrest) anhand des primitiven Datentyps `int` + */ +static void test03_1_2_a_grundrechenarten() { + // Addition + { + { + static_assert(3 == 1+2); + assert(3 == 1+2); // NOLINT (intentional) + } + { + const int i = 6; // positiver Wert + const int j = 2; // positiver Wert + const int k = i + j; + assert(8 == k); + } + { + const int i = +6; // positiver Wert + const int j = -2; // negativer Wert!! + const int k = i + j; + assert(4 == k); + } + } + // Subtraktion + { + const int i = 6; + const int j = 2; + const int k = i - j; + assert(4 == k); + } + // Multiplikation + { + const int i = 6; + const int j = 2; + const int k = i * j; + assert(12 == k); + } + // Division + { + { + const int i = 6; + const int j = 2; + const int k = i / j; + assert(3 == k); + } + { + const int i = 7; + const int j = 2; + const int k = i / j; // as real number: 7/2=3.5, but integer simply cuts off the floating point + assert(3 == k); + } + } + // Modulo (Divisionsrest) + { + { + const int i = 6; + const int j = 2; + const int k = i % j; + const int l = i - ( i / j ) * j; // Modulo definition, i.e. Divisionsrest + assert(l == k); + assert(0 == k); + } + { + const int i = 7; + const int j = 2; + const int k = i % j; + const int l = i - ( i / j ) * j; // Modulo definition, i.e. Divisionsrest + assert(l == k); + assert(1 == k); + } + } +} + +/** + * Implement pre- and post-increment and -decrement. + * + * Also address call-by-value and call-by-reference semantics. + */ + +/** + * Non-functional pre-increment using call-by-value. + * @param v integer _value_ to increment, copied from caller + * @return + */ +static int preIncr_call_by_value(int v) { + return ++v; +} + + +/** + * `return ++v` + * + * Call-by-value does not work. + * + * Call-by-reference works! + * + * @param v + * @return rueckgabewert der pre-incr operation + */ +static int preIncr(int &ref) { + ref = ref + 1; + return ref; +} + +static int incrPost(int &ref) { + const int v = ref; + ref = v + 1; + return v; +} + +static int preDecr(int &ref) { + ref = ref - 1; + return ref; +} + +/** decrPost variant 1 */ +static int decrPost(int &ref) { + const int v = ref; + ref = v - 1; + return v; +} + +/** + * Unary-Operatoren (1 Argument) anhand des primitiven Datentyps `int` + */ +static void test03_1_2_b_unary_post_prefix() { + { + int i = 1; + int j = 1; + const int c1 = ++i; // c1: rueckgabewert der pre-incr operation + const int c2 = j++; // c2: rueckgabewert der post-incr operation + assert( 2 == c1); + assert( 1 == c2); + assert( 2 == i); + assert( 2 == j); + } + // Prefix erhoehe (increment) und veringere (decrement) + { + int i = 6; + ++i; + assert(7 == i); + --i; + assert(6 == i); + + // !!!! + assert(6 == i); // Inhalt von `i` ist `6` + assert(7 == ++i); // Inhalt von `i` wird erhoeht, dann zurueckgegeben! // NOLINT + assert(7 == i); // Selber Wert + } + // Demo preIncr_call_by_value() failure + { + const int i = 6; + assert(7 == preIncr_call_by_value(i)); // OK + assert(6 == i); // Nope, not a valid pre-increment implementation! + } + { + int i = 6; + preIncr(i); // call-by-reference + assert(7 == i); + preDecr(i); // call-by-reference + assert(6 == i); + + // !!!! + assert(6 == i); // Inhalt von `i` ist `6` + assert(7 == preIncr(i)); // Inhalt von `i` wird erhoeht, dann zurueckgegeben! + assert(7 == i); // Selber Wert + } + + // Postfix erhoehe (increment) und veringere (decrement) + { + int i = 6; + i++; + assert(7 == i); + i--; + assert(6 == i); + + // !!!! + assert(6 == i); // Inhalt von `i` ist `6` + assert(6 == i++); // Inhalt von `i` wird zurueckgegeben, dann erhoeht! // NOLINT + assert(7 == i); // Nun ist der Inhalt von `i`erhoeht + } + { + int i = 6; + preIncr(i); + assert(7 == i); + decrPost(i); + assert(6 == i); + + // !!!! + assert(6 == i); // Inhalt von `i` ist `6` + assert(6 == incrPost(i)); // Inhalt von `i` wird zurueckgegeben, dann erhoeht! + assert(7 == i); // Nun ist der Inhalt von `i`erhoeht + } +} + +/** + * Zuweisungs-Operatoren inklusive der 4 Grundrechenarten anhand des primitiven Datentyps `int` + */ +static void test03_1_2_c_zuweisung() { + // Einfache Zuweisung + { + int i = 6; + assert(6 == i); + i = 7; + assert(7 == i); + } + + // Addition-Zuweisung + { + int i = 6; + i += 4; + assert(10 == i); + } + // Subtraktion-Zuweisung + { + int i = 6; + i -= 4; + assert(2 == i); + } + // Multiplikation-Zuweisung + { + int i = 6; + i *= 4; + assert(24 == i); + } + // Division-Zuweisung + { + int i = 6; + i /= 2; + assert(3 == i); + } + // Modulo-Zuweisung + { + { + int i = 6; + i %= 2; + assert(0 == i); + } + { + int i = 7; + i %= 2; + assert(1 == i); + } + } +} + +/** + * Operatoren der logischen Vergleiche anhand des primitiven Datentyps `int` + */ +static void test03_1_2_d_vergleich() { + // Gleichheit (equality) + { + const int i = 8; + const int j = 8; + const int k = 9; + assert(i == j); + + assert(i != k); + } + // Relational + { + const int i = 8; + const int j = 8; + const int k = 9; + assert(false == (i < j)); + assert(true == (i <= j)); + assert(true == (i >= j)); + assert(false == (i > k)); + + assert(true == (i < k)); + assert(true == (i <= k)); + assert(false == (i >= k)); + assert(false == (i > k)); + } +} + +/** + * Operatoren der logischen Verknuepfung anhand des primitiven Datentyps `int` und `boolean` + */ +void test03_1_2_e_logisch() { + // Logisch-Und (and) als auch Logisch-Oder (or) + { + const int i = 8; + const int j = 8; + const int k = 9; + const bool b0 = i==j; + const bool b1 = i==k; + + assert(true == ( b0 && !b1)); + assert(false == (!b0 || b1)); + + assert(true == (i == j && i != k)); + assert(false == (i != j || i == k)); + } +} + +/** + * Integer overflow aware addition returning true if overflow occurred, + * otherwise false having the result stored in res. + * + * Implementation follows API of [GCC Integer Overflow Builtins](https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html). + * + * @param a operand a + * @param b operand b + * @param res storage for result + * @return true if overflow, otherwise false + */ +static bool add_overflow(const short a, const short b, short &res) +{ + // overflow: a + b > R+ -> a > R+ - b, with b >= 0 + // underflow: a + b < R- -> a < R- - b, with b < 0 + + if ( ( b >= 0 && a > std::numeric_limits<short>::max() - b ) || + ( b < 0 && a < std::numeric_limits<short>::min() - b ) ) + { + return true; + } else { + res = (short)(a + b); + return false; + } +} + +/** + * Primitive data types incl. under- and overflow. + */ +static void test03_1_3_primitive_underoverflow() { + // trigger (ausloesung) short over- and underflow: no practical use-case + { + const short one = 1; + short i = std::numeric_limits<short>::max(); + i = (short)(i + one); // overflow + assert(std::numeric_limits<short>::min() == i); + + i = std::numeric_limits<short>::min(); + i = (short)(i - one); // underflow + assert(std::numeric_limits<short>::max() == i); + } + // trigger short overflow: no practical use-case + { + const short a = std::numeric_limits<short>::max() - 10; + const short b = 11; // a + b -> overflow + short c; + { + // How to make this safe?? + c = (short)(a + b); + } + (void) c; + // assert(a + b != c); // NOLINT - tautological-constant-out-of-range-compare + } + // Safe math using add_overflow() + // similar to [GCC Integer Overflow Builtins](https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html), + // i.e. detecting under- and overflow for add operation on two integer. + { + short res = 0; // one short value + assert(false == add_overflow((short) 0, (short) 0, res)); + assert((short)0 == res); + + res = 0; + assert(false == add_overflow(std::numeric_limits<short>::max(), (short) 0, res)); + assert(std::numeric_limits<short>::max() == res); + + res = 0; + assert(true == add_overflow(std::numeric_limits<short>::max(), (short) 1, res)); + assert((short)0 == res); + + res = 0; + assert(false == add_overflow(std::numeric_limits<short>::max(), (short)-1, res)); + assert(std::numeric_limits<short>::max()-1 == res); + + res = 0; + assert(false == add_overflow(std::numeric_limits<short>::min(), (short) 1, res)); + assert(std::numeric_limits<short>::min()+1 == res); + + res = 0; + assert(true == add_overflow(std::numeric_limits<short>::min(), (short)-1, res)); + assert((short)0 == res); + + res = 0; + assert(true == add_overflow((short) -1,std::numeric_limits<short>::min(), res)); + assert((short)0 == res); + } +} + +/** + * Primitive Datentypen und Literale (Konstante) + */ +static void test03_1_4_immutable() { + // Unveraenderbare (Immutable) Variablen, i.e. Konstante + const int const_i0 = 1; + // const_i0 = const_i0 + 1; // FEHLER + assert(1 == const_i0); +} + +struct Class03_2_1 { + int var = 0; + + int erhoeheUm(const int a) { + var += a; + return var; + } +}; + +static void test03_2_1_method_instanz() { + Class03_2_1 instance; + assert(2 == instance.erhoeheUm(2)); + assert(2 == instance.var); + assert(5 == instance.erhoeheUm(3)); + assert(5 == instance.var); +} + +struct Class03_2_2 { + static int addiere(const int a, const int b) { + return a+b; + } +}; + +static void test03_2_2_method_static() { + assert(5 == Class03_2_2::addiere(2, 3)); +} + +/** + * Manual definition + */ +class Class03_2_3a { + public: + // Konstante .. 3.3.2 Globale (Klassen) Variablen (immutable) + static const int ONE = 1; // global immutable (constant) + // 3.3.2 Globale (Klassen) Variablen + static int global_var01; // global static, storage allocation see below post class declaration (explicit initialization) + static int global_var02; // global static, storage allocation see below post class declaration (default initialization) + + // 3.3.3 Klassen Instanz Variable (Field) + int field_member_01 = 1; // explicit initialization (init), belegt speicher pro instanz (Object) + int field_member_02 = 0; // explicit initialization (init), belegt speicher pro instanz (Object) + + /** + * Default Constructor + * + * A constructor METHOD is Identified by: + * - No declared return value, nor return statement + * - Class name _is_ method name + */ + Class03_2_3a() noexcept { + std::cout << "Class03_2_3a: Default-Constructor" << std::endl; + } + + /** + * Custom Constructor 1 + * @param a init value for field_member_01 + * @param b init value for field_member_02 + */ + Class03_2_3a(const int a, const int b) noexcept + : field_member_01(a), + field_member_02(b) + { + std::cout << "Class03_2_3a: Custom-Constructor-1" << std::endl; + } + + /// Copy Constructor + /// default: `constexpr Class03_2_3a(const Class03_2_3a &o) noexcept = default;` + Class03_2_3a(const Class03_2_3a &o) noexcept + : field_member_01(o.field_member_01), + field_member_02(o.field_member_02) + { + std::cout << "Class03_2_3a: Copy-Constructor" << std::endl; + } + + /// Move Constructor + Class03_2_3a(Class03_2_3a &&o) noexcept = default; + /// Copy-Assignment operator + Class03_2_3a& operator=(const Class03_2_3a &o) noexcept = default; + /// Move-Assignment operator + Class03_2_3a& operator=(Class03_2_3a &&o) noexcept = default; + + /** + * Destructor + * + * A destructor METHOD is Identified by: + * - No declared return value, nor return statement + * - Leading ~ (tilde) + * - Class name _is_ method name + */ + ~Class03_2_3a() noexcept { + std::cout << "Class03_2_3a: Destructor" << std::endl; + } + + void increment() noexcept { + ++field_member_01; + // 'this' is a pointer to this instance (Object), where method `incement` is applied to + ++this->field_member_02; + } + + void reset() noexcept { + field_member_01 = 1; + field_member_02 = 0; + } + + /** Two way comparison operator */ + bool operator==(const Class03_2_3a& o) const noexcept { + return field_member_01 + field_member_02 == o.field_member_01 + o.field_member_02; + } + + /** Three way std::strong_ordering comparison operator */ + std::strong_ordering operator<=>(const Class03_2_3a& o) const noexcept { + const int s1 = field_member_01 + field_member_02; + const int s2 = o.field_member_01 + o.field_member_02; + if( s1 == s2 ) { + return std::strong_ordering::equal; + } else if( s1 < s2 ) { + return std::strong_ordering::less; + } else { + return std::strong_ordering::greater; + } + } + + /** Compare function returns 0 if equal, -1 if *this < b and 1 if *this > b. */ + int compareTo(const Class03_2_3a& o) const noexcept { + const int s1 = field_member_01 + field_member_02; + const int s2 = o.field_member_01 + o.field_member_02; + if( s1 == s2 ) { + return 0; + } else if( s1 < s2 ) { + return -1; + } else { + return 1; + } + } + + constexpr std::size_t hashCode() const noexcept { + // 31 * x == (x << 5) - x + std::size_t h = 31 + field_member_01; + h = ( ( h << 5 ) - h ) + field_member_01; + return h; + } + + /// std::ostream (stream out) free operator overload + std::ostream& streamout(std::ostream& out) const { + return out << "Class03_2_3a[ref 0x" << std::hex << this + << std::dec + << ", 01 " << field_member_01 + << ", 02 " << field_member_01 + << "]"; + } + + std::string toString() const { + std::stringstream ss; + streamout(ss); + return ss.str(); + } +}; + +int Class03_2_3a::global_var01 = 1; // storage for global static and explicit initialization (init) +int Class03_2_3a::global_var02; // storage for global static and w/o initialization + +/// std::ostream (stream out) free operator overload +std::ostream& operator<<(std::ostream& out, const Class03_2_3a& v) { + return v.streamout(out); +} + +static void test03_2_3a_class() { + // 0: Analog to instances of primitive types, class instances are shown below this block + { + // i1 (locale variable) ist der Name fuer den Speicherbereich der Groesse 'int' + const int i1 = 0; + assert( 0 == i1 ); + + int i2 = 0; + assert( 0 == i2 ); + i2 = 1; + assert( 1 == i2 ); + } + // 1a: Default Constructor, detailing reference and automatic storage (stack) instance + { + // Create 1st instance of type Class03_2_3a in automatic storage (stack) + // + // 1) Compiler allocates stack-memory (memO1) for one instance of type Class03_2_3a + // 2) Initialize this stack-memory (memO1) using Class03_2_3a's default constructor + Class03_2_3a i1; + + // 3) Use memory (memO1) reference of this instance in o1 + Class03_2_3a &o1 = i1; + + // instance variable i1 and its reference o1 use same memory address + assert(&i1 == &o1); + + assert(1 == i1.field_member_01); + assert(0 == i1.field_member_02); + assert(1 == o1.field_member_01); + assert(0 == o1.field_member_02); + + // Create 2nd instance of type Class03_2_3a in stack-memory + // + // 1) Compiler allocates stack-memory (memO2) for one instance of type Class03_2_3a + // 2) Initialize this stack-memory (memO2) using Class03_2_3a's default constructor + Class03_2_3a i2; + + // 3) Use memory (memO2) reference of this instance in o2 + Class03_2_3a &o2 = i2; + + // + // NOTE: This created instance i2 within memO2 in stack-memory (reference stored in o2) + // is different than i1 within memO1 above (reference stored in o1) + assert(1 == i2.field_member_01); + assert(0 == i2.field_member_02); + assert(1 == o2.field_member_01); + assert(0 == o2.field_member_02); + + assert(i1 == i2); + assert(0 == i1.compareTo(o2)); + assert(&i1 != &i2); // fast ref-check + + assert(o1 == o2); + assert(0 == o1.compareTo(o2)); + assert(&o1 != &o2); // fast ref-check + + o1.increment(); // veraendere Object o1 + assert(2 == o1.field_member_01); + assert(1 == o1.field_member_02); + assert(o1 != o2); + assert( 1 == o1.compareTo(o2)); + assert(-1 == o2.compareTo(o1)); + + o1.reset(); + assert(o1 == o2); + assert(0 == o1.compareTo(o2)); + } + // 1b: Default Constructor, detailing reference and heap-memory instance + { + // Create 1st instance of type Class03_2_3a in heap memory + // + // 1) Allocate heap-memory (memO1) for one instance of type Class03_2_3a + // 2) Initialize this memory (memO1) using Class03_2_3a's default constructor + // 3) Store memory (memO1) reference of this instance in o1 + // + // o1 ist ein ZEIGER (Referenz, Pointer) auf eine Instanz (Object) (memO1) + // des Typs Class03_2_3a. + // + // Object o1 (locale variable) selber ist mutable, aber die Referenz o1 ist immutable! + Class03_2_3a *o1 = new Class03_2_3a(); + assert(1 == o1->field_member_01); + assert(0 == o1->field_member_02); + + // Create 2nd instance of type Class03_2_3a in heap memory + // + // 1) Allocate heap-memory (memO2) for one instance of type Class03_2_3a + // 2) Initialize this memory (memO2) using Class03_2_3a's default constructor + // 3) Store memory (memO2) reference of this instance in o2 + // + // NOTE: This created instance memO2 in heap memory (reference stored in o2) + // is different than memO1 above (reference stored in o1) + // 2. Class03_2_3a instance o2 (locale variable): Heap-Memory allocation (-> new reference), initialization via constructor. + Class03_2_3a *o2 = new Class03_2_3a(); + assert(1 == o2->field_member_01); + assert(0 == o2->field_member_02); + + assert(*o1 == *o2); + assert(0 == o1->compareTo(*o2)); + assert(o1 != o2); // fast ref-check + + o1->increment(); // veraendere Object o1 + assert(2 == o1->field_member_01); + assert(1 == o1->field_member_02); + assert(*o1 != *o2); + assert( 1 == o1->compareTo(*o2)); + assert(-1 == o2->compareTo(*o1)); + + o1->reset(); + assert(*o1 == *o2); + assert(0 == o1->compareTo(*o2)); + + /// Manually delete heap-memory (unsafe!) + delete o1; + delete o2; + } + // 2: Default Constructor for one single stack-memory instance, shared by two reference holder + { + Class03_2_3a i1; + Class03_2_3a &o1 = i1; + assert(1 == o1.field_member_01); + assert(0 == o1.field_member_02); + { + // o1_b is assigned the memory (memO1) reference only, i.e. o1! + // At this point there is only one actual instanz of Class03_2_3a alive, (memO1). + const Class03_2_3a &o1_b = o1; // shares same instance i1 via reference + assert(&o1 == &o1_b); // fast ref-check + assert(o1 == o1_b); // deep comparison + assert(0 == o1.compareTo(o1_b)); + + // because o1 and o1_b point to the same heap-memory instance of type Class03_2_3a + // ... + o1.increment(); // veraendere Object o1 + assert(o1 == o1_b); + } + } + // 3: Custom Constructor only differs to Default Constructor using its custom initialization + { + // 1) Allocate stack-memory for one instance of type Class03_2_3a + // 2) Initialize this memory using Class03_2_3a's custom constructor + const Class03_2_3a o1(2, 3); + assert(2 == o1.field_member_01); + assert(3 == o1.field_member_02); + + const Class03_2_3a o2(2, 3); + assert(2 == o2.field_member_01); + assert(3 == o2.field_member_02); + + assert(o1 == o2); + assert(0 == o1.compareTo(o2)); + } + // 4: Custom Copy Constructor only differs to Default Constructor using its special custom initialization (deep-copy) + { + Class03_2_3a o1; // default ctor (Constructor): 1st instance in stack-memory of type Class03_2_3a + Class03_2_3a o2(o1); // copy ctor: 2nd instance in heap-memory of type Class03_2_3a + assert(o1 == o2); // same content/value, but different heap-memory instances + assert(&o1 != &o2); // different heap-memory instances, hence different references (pointer to heap-memory) + std::cout << "4.0: o1 " << o1 << std::endl; + std::cout << "4.0: o2 " << o2 << std::endl; + + o1.increment(); // veraendere Object o1 + assert(o1 != o2); + std::cout << "4.1: o1 " << o1 << std::endl; + + o2.increment(); + assert(o1 == o2); + std::cout << "4.1: o2 " << o2 << std::endl; + } +} + +/// RAII owner of heap allocated Class03_2_3a instance similar to std::unique_ptr +class Class03_2_3a_Owner +{ + public: + Class03_2_3a *m_o; + + Class03_2_3a_Owner() noexcept + : m_o(nullptr) {} + + Class03_2_3a_Owner(Class03_2_3a* o) noexcept + : m_o(o) {} + + /// Deleted Copy Constructor + Class03_2_3a_Owner(const Class03_2_3a_Owner &o) noexcept = delete; + /// Deleted Copy-Assignment operator + Class03_2_3a_Owner& operator=(const Class03_2_3a_Owner &o) noexcept = delete; + + /// Default Move Constructor + Class03_2_3a_Owner(Class03_2_3a_Owner &&o) noexcept = default; + /// Default Move-Assignment operator + Class03_2_3a_Owner& operator=(Class03_2_3a_Owner &&o) noexcept = default; + + ~Class03_2_3a_Owner() noexcept { + if( nullptr != m_o ) { + delete m_o; + m_o = nullptr; + } + } + + /// may return nullptr, no exception + Class03_2_3a* ptr() noexcept { return m_o; } + + /// throws std::runtime_error if nullptr! + Class03_2_3a& ref() { + if( nullptr == m_o ) { + throw std::runtime_error("nullptr"); + } + return *m_o; + } +}; + +/// Resource acquisition is initialization +static void test03_2_3b_class_RAII() { + // Lebenszyklus (Lifecycle / Object-Duration) + std::cout << "test03_2_3b_class_RAII: 0.0" << std::endl; + { + std::cout << "test03_2_3b_class_RAII: 1.0" << std::endl; + // 1) Allocate heap-memory for one instance of type Class03_2_3a + // 2) Initialize this memory using Class03_2_3a's default constructor + // 3) Store memory reference of this instance in k1 + const Class03_2_3a_Owner k1(new Class03_2_3a()); + std::cout << "test03_2_3b_class_RAII: 1.1" << std::endl; + } // delete local reference-holder _WITH_ releasing heap-memory of Class03_2_3a + std::cout << "test03_2_3b_class_RAII: 0.1" << std::endl; +} + +// 3.3 Speicher-Klassen von Variablen +struct Class03_3 { + // 3.3.2 Globale (Klassen) Variablen + static int lala; + // Konstante .. 3.3.2 Globale (Klassen) Variablen (immutable) + static const int lulu = 0; + + // 3.3.3 Klassen Instanz Variable (Field) + int lili = 0; +}; +int Class03_3::lala = 0; + +/** + * 3.3 Speicher-Klassen von Variablen + */ +static void test03_3_storage_class() { + Class03_3 o1; + Class03_3 o2; + + // 3.3.3 Klassen Instanz Variable (Field) + assert(o1.lili == o2.lili); // gleich werte beider instanzen + + // 3.3.2 Globale (Klassen) Variablen + assert(o1.lala == o2.lala); // gleicher wert der gemeinsamen globalen instanz + assert(Class03_3::lala == Class03_3::lala); // ditto + assert(o1.lulu == o2.lulu); // ditto + + // lili wert der instanz o1 auf 10 gesetzt (und nicht von der instanz o1) + o1.lili = 10; + assert(o1.lili != o2.lili); // o1.lili == 10, o2.lili == 0 + + // lala wert der Klasse (aller instanzen) auf 10 gesetzt + o1.lala = 10; + assert(o1.lala == o2.lala); // gleich + assert(Class03_3::lala == Class03_3::lala); // ditto +} + +static void test03_5_arrays() { + /** + * sizeof(int) == 4 (bytes) + * int a[] = new int[4]; + * 0x0000: a[0] + * 0x0004: a[1] + * 0x0008: a[2] + * 0x000C: a[3] + */ + { + const int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + assert(1+10 == a[0] + a[9]); + assert(2 == a[1]); + assert(9 == a[8]); + assert(10 == sizeof(a) / sizeof(int)); // length of the array == 10! IMMUTABLE! // NOLINT + + { + const int (&b)[10] = a; // b references array storage instance `a` + assert(&a == &b); + } + } + // variant 1 + { + const int grades[] = { 1, 2, 3, 4, 4, 3, 2, 1, 5, 3, + 1, 2, 3, 3, 2, 1, 5 }; + const size_t length = sizeof(grades) / sizeof(int); + assert(17 == length); + int sum = 0; + for(size_t i=0; i<length; ++i /* for-tail-statement */) { // NOLINT (intended) + sum += grades[i]; + } + const float median = (float)sum / (float)length; + std::cout << "Median: " << median << " of " << length << " students. Sum " << sum << std::endl; + } + // variant 2 + { + const int grades[] = { 1, 2, 3, 4, 4, 3, 2, 1, 5, 3, + 1, 2, 3, 3, 2, 1, 5 }; + const size_t length = sizeof(grades) / sizeof(int); + assert(17 == length); + int sum = 0; + size_t i = 0; + while( i<length ) { + sum += grades[i]; + ++i; // for-tail-statement + } + const float median = (float)sum / (float)length; + std::cout << "Median: " << median << " of " << length << " students. Sum " << sum << std::endl; + } + // variant 3 + { + const int grades[] = { 1, 2, 3, 4, 4, 3, 2, 1, 5, 3, + 1, 2, 3, 3, 2, 1, 5 }; + const size_t length = sizeof(grades) / sizeof(int); + assert(17 == length); + int sum = 0; + size_t i = 0; + while( i<length ) { + sum += grades[i++]; + } + const float median = (float)sum / (float)length; + std::cout << "Median: " << median << " of " << length << " students. Sum " << sum << std::endl; + } +} + +/** + * Programmfluss-Statement: Branches (if, switch-case, conditional-op) + */ +static void test04_1_branch() { + // branches: if + { + int state = -1; + const int a = 0; + + // simple if-else + { + // Note that we place the immutable literal (r-value) on the left-side + // of the equality operation '0 == a', + // which avoids accidental assignment of a mutable l-value if typo 'a = 0'. + if( 0 == a ) { + // true-statement: executed if 'a' contains '0' + state = 1; + assert(true); // NOLINT (intended) + } else { + // false-statement: executed if 'a' does not contain '0' + assert(false); // unreachable, dead code + } + assert(1 == state); + } + state = -1; // NOLINT(clang-analyzer-deadcode.DeadStores) intended + + // too complicated if-else-if usage + { + if( 0 == a ) { + // true-statement: executed if 'a' contains '0' + state = 1; + assert(true); // NOLINT (intended) + } else { + // false-statement: executed if 'a' does not contain '0' + if( 1 == a ) { + // true-statement: executed if 'a' contains '1' + assert(false); // unreachable, dead code + } else { + // false-statement: executed if 'a' does not contains '0' nor '1' + assert(false); // unreachable, dead code + } + } + assert(1 == state); + } + state = -1; // NOLINT(clang-analyzer-deadcode.DeadStores) intended + + // desired if-else-if usage + { + if( 0 == a ) { + // true-statement: executed if 'a' contains '0' + state = 1; + assert(true); // NOLINT (intended) + } else if( 1 == a ) { + // true-statement: executed if 'a' contains '1' + assert(false); // unreachable, dead code + } else { + // false-statement: executed if 'a' does not contains '0' nor '1' + assert(false); // unreachable, dead code + } + assert(1 == state); + } + state = -1; // NOLINT(clang-analyzer-deadcode.DeadStores) intended + + // Note that the expression `0 == a` is a boolean expression, + // i.e. resolved to either `true` or `false`. + const bool b0 = 0 == a; + const bool b1 = 1 == a; + + // Note that the expression for `if` and `while` are boolean expressions. + // + // Below we use the pre-computed boolean results. + if( b0 ) { + // true-statement: executed if b0 is true, i.e. 'a' contains '0' + state = 1; + assert(true); // NOLINT (intended) + } else if( b1 ) { + // false-statement: executed if b1 is true, i.e. 'a' contains '1' + assert(false); // unreachable + } + + assert(1 == state); + state = -1; + + // to void errors by missing braces USE braces + { + // Desired + int lala = 0; + + if( b1 ) { state = 1; } + assert(-1 == state); + assert(0 == lala); + + if( b1 ) { state = 2; lala = 2; } + assert(-1 == state); + assert(0 == lala); + + // Ugly + state = -1; + lala = 0; // NOLINT(clang-analyzer-deadcode.DeadStores) intended + + if( b1 ) state = 1; + assert(-1 == state); + + if( b1 ) state = 2; lala = 2; + assert(-1 == state); + // Error: assert(0, lala); // ERROR: 'lala == 2' + assert(2 == lala); // Semantical programming error? Typo? ... + } + } + + // branches: switch + { + int state = -1; + const int a = 0; + + // Demo using switch-case instead if-else-if _comparing_ _literals_ (constants) + { + // desired if-else-if usage + { + if( 0 == a ) { + // true-statement: executed if 'a' contains '0' + state = 1; + assert(true); // NOLINT (intended) + } else if( 1 == a ) { + // true-statement: executed if 'a' contains '1' + assert(false); // unreachable, dead code + } else { + // false-statement: executed if 'a' does not contains '0' nor '1' + assert(false); // unreachable, dead code + } + assert(1 == state); + } + assert(1 == state); + state = -1; // NOLINT(clang-analyzer-deadcode.DeadStores) intended + } + { + switch( a ) { + case 0: + // executed if 'a' contains '0' + state = 1; + assert(true); // NOLINT (intended) + break; // ends code for this case + case 1: + // executed if 'a' contains '1' + { + // use an inner block-statement to allow local case resources + const int v = 1; + std::cout << "branch1." << v << std::endl; + } + assert(false); // unreachable + break; // ends code for this case + case 2: + // executed if 'a' contains '2' + // and falls through to default case code + assert(false); // unreachable + // [[fallthrough]]; + default: + // executed if none of the above cases matches + assert(false); // unreachable + break; // ends code for this case + } + assert(1 == state); + } + } + + // branches: conditional operator '?' + { + { + const int a = 0; + + // initialized with '0' if 'a' contains '0', otherwise initialized with '1' + const char c = ( 0 == a ) ? '0' : '1'; + + assert('0' == c); + } + { + int x = 2; + + int l; + if( 0 < x ) { + l = 1; + } else { + l = 0; + } + int r; + if( x < 0 ) { + r = 1; + } else { + r = 0; + } + int result1 = l - r; + assert(1 == result1); + + int result2 = (0 < x ? 1 : 0) - (x < 0 ? 1 : 0); + assert(result1 == result2); + + // x<-1 -> result=-1 + // x=-1 -> result=-1 + // + // x= 0 -> result= 0 + // + // x= 1 -> result= 1 + // x= 2 -> result= 1 + // x> 2 -> result= 1 + // + // sign(x): Vorzeichen-Funktion + // Returns -1 bei x<0, 0 bei x==0 und 1 bei x>0 + } + } +} + +/** + * Programmfluss-Statement: Loops (while, do-while, for, break) + */ +static void test04_2_loops() { + const int loop_count = 3; + // Same loop as while + { + // while loop, an exploded for-loop (see below) + int v=10; + int i=0; /* instantiation and initialization of loop variable */ + while( i < loop_count /* while condition */ ) { + ++v; + i = i + 1; /* tail expression */ + } + assert(loop_count == i); + assert(10+loop_count == v); + } + // Same loop as do-while + { + // do-while loop - executed at least once + int v=10; + int i=0; /* instantiation and initialization of loop variable */ + do { + ++v; + i = i + 1; /* tail expression */ + } while( i < loop_count /* while condition */ ); + assert(loop_count == i); + assert(10+loop_count == v); + } + // Same loop as for (1) + { + int v=10; + int i; /* instantiation of loop variable*/ + for(i=0 /* initialization of loop variable*/; + i<loop_count /* while condition */; + ++i /* tail expression */) + { + ++v; + } + // `i` is still in scope! + assert(loop_count == i); + assert(10+loop_count == v); + } + // Same loop as for (2) + { + int v=10; + /* instantiation and initialization of loop variable; while condition; tail expression */ + for(int i=0; i<loop_count; ++i) { + ++v; + } + // `i` is out of scope! + // assert(loop_count, i); + assert(10+loop_count == v); + } + // Using break within a loop (bad style, rarely unavoidable) + { + int v=10; + int i=0; /* instantiation and initialization of loop variable */ + while( true /* while condition: forever */ ) { + if( i >= loop_count ) { // inverted `i<loop_count` + break; // exit loop + } + ++v; + i = i + 1; /* tail expression */ + } + assert(loop_count == i); + assert(10+loop_count == v); + } + // loop through an array: forward + { + // Calculate the sum of all array elements + const int a[] = { 1, 2, 3, 4 }; + const size_t length = sizeof(a) / sizeof(int); + int sum = 0; + for(size_t i=0; i<length; ++i) { + sum += a[i]; + } + assert(10 == sum); + } + // loop through an array: backwards + { + // Calculate the sum of all array elements + const int a[] = { 1, 2, 3, 4 }; + const size_t length = sizeof(a) / sizeof(int); + int sum = 0; + for(size_t i=length; i-->0; ) { // NOTE: We avoid underflow of unsigned type size_t + sum += a[i]; + } + assert(10 == sum); + } +} + +int main(int /*argc*/, const char* /*argv*/[]) { + std::cout << "cs0104_lesson_01: START" << std::endl; + test01(); + test02_3_block_statement(); + test03_1_1_literals(); + test03_1_2_a_grundrechenarten(); + test03_1_2_b_unary_post_prefix(); + test03_1_2_c_zuweisung(); + test03_1_2_d_vergleich(); + test03_1_3_primitive_underoverflow(); + test03_1_4_immutable(); + test03_2_1_method_instanz(); + test03_2_2_method_static(); + test03_2_3a_class(); + test03_2_3b_class_RAII(); + test03_3_storage_class(); + test03_5_arrays(); + test04_1_branch(); + test04_2_loops(); + std::cout << "cs0104_lesson_01: END - OK" << std::endl; +} |