layout: true name: blank styling: styling.css styling-by: Martin Weitzel .stylehint[ Styled with [{{styling}}]({{styling}}) by {{styling-by}} ] --- layout: true name: plain copyright: (CC) BY-SA branding: [Dipl.-Ing. Martin Weitzel](http://tbfe.de) customer: [for MicroConsult Training & Consulting GmbH](http://microconsult.de) {{header}} .pagefooter[ {{copyright}}: {{branding}} {{customer}} .microconsult-logo[] ] --- layout: true name: linkinfo copyright: (CC) BY-SA branding: [Dipl.-Ing. Martin Weitzel](http://tbfe.de) customer: [for MicroConsult Training & Consulting GmbH](http://microconsult.de) {{header}} .infographic[ [](InfoGraphics/{{graphic}}.png "Click to open – add [CTRL+] SHIFT for new [tabbed] window") ] .pagefooter[ {{copyright}}: {{branding}} {{customer}} .microconsult-logo[] ] --- layout: true name: withinfo copyright: (CC) BY-SA branding: [Dipl.-Ing. Martin Weitzel](http://tbfe.de) customer: [for MicroConsult Training & Consulting GmbH](http://microconsult.de) {{header}} .infolink.right[ [Click here for Info-Graphic {{graphic}}](InfoGraphics/{{graphic}}.png "add [CTRL+] SHIFT for own [tabbed] window") {{section}} ] .pagefooter[ {{copyright}}: {{branding}} {{customer}} .microconsult-logo[] ] --- template: blank name: frontmatter .title[ C++ Topics TKI ] .subtitle[ Tuesday: More C++-Refresh and Class/Object Relations ] .author.pull-left[ Durchführung: Dipl.-Ing. Martin Weitzel Technische Beratung für EDV http://tbfe.de ] .client.pull-right[ Im Auftrag von: MicroConsult Training & Consulting GmbH http://www.microconsult.com ] --- template: plain class: agenda name: agenda header: ## Agenda This agenda and the following pages show a proposed path through the topics selected for the next days ------------------------------------------------------------------------------- * [More C++ Quick Rfresh ](#more_cpp_refresh) * [Class Relations ](#base_and_derived) ------------------------------------------------------------------------------- Variations and extensions can be agreed any time. --- template: plain name: more_cpp_refresh header: ## More C++ Quick Refresh * [References ](#references) * [Default Arguments ](#default_args) * [Function Overloading ](#function_ovld) * [Operator Overloading ](#operator_ovld) It should be understood that "under the hood", C and C++ are both mapped to the hardware in the same, basic way. .N.center[ So, from the low-level view, they use the same [Execution Model](#execution_model). ] --- template: plain name: arithmetic_types header: ### Arithmetic Types C++ has all the arithmetic types that C has, including a special type for binary logic (`bool` with value `true` and `false`). Conversions between arithmetic types take place automatically:._[] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ int i = -12; // always OK unsigned u = i; // OK (maybe compile time warning) short t{32768}; // new C++11 style initialization, // overflow error at compile time if // short is 16 bit two-s complement char c = t+122; // OK (maybe compile time warning) if (c) … // every value not 0 is taken as true ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++11 allows to use `auto` as typename; the type of the variable will be the type of the initializing expression (without `const` and *reference*): ``` auto x = 3.1415; // x will have type double auto y = 1.4142f; // y will have type float ``` .F[: There may be a warning if loss of information is possible. This is typically based on the types involved, not on some analysis of actual values (which may or may not be fully determinable at compile time). ] --- template: plain name: uniform_initialization header: #### Brace Initialization C++11 also extended the initialization syntax * to allow * initial values be written in a pair of curly braces anywhere._[] and * (only in this case) omitting the equals sign between the variable name and the opening brace. * provided a whole new (template) type `std::initializer_list
` * written as comma-separated list of identical or compatible types * enclosed in a pair of curly braces. .W[ While the original intent was to "simplify" the initialisation syntax, there were some unexpected stumbling-stones in combination with `auto`. ] .F[: Traditionally C and C++ supported this syntax only for structures and native arrays. ] --- template: plain header: #### Enforcing Conversions / Suppressing Warnings Conversions need sometimes to be enforced with cast operations:._[] ``` int x = 9; int y = 5; double d = static_cast
(x) / static_cast
(y); // instead of C-style cast: (double)x / (double)y ``` Also, cast operations are sometimes used with the intent to suppress a warning: ``` short s = static_cast
(d); ``` .N[ Which warning is issued under what circumstance and how it can be suppressed is always compiler specific. ] .F[: C-style casts are still part of the C++ language but a deprecated language feature because they are hard to spot and the intention is not as much part of the syntax as with the C++-style cast, for which more examples will be shown later. ] --- template: plain name: const_qualifiers header: ### Qualifiers Implying Immutability There are two qualifiers._[] making a variable" immutable: * `const` is applicable to variables only: * the compiler will not generate code that modifies the initial value * the linker may or may not put the variable in read-only memory * `constexpr` is applicable to variables and functions: * for a variable it guarantees the initial value can be calculated at compile time * for functions it does the calculations at compile time, if possible .F[: `const` was originally introduced with C++ but also became part of the C89 standard – hence today, 25 years later, it may well be reclaimed as a C++ addition but few people still know today. Nevertheless it seems even today C++ programs make more use of this feature than C programs. `constexpr` was introduced with C++11. ] --- template: plain header: #### `const` Is To Ensure Immuability A variable may optionally be qualified with `const`:._[] .pull-left[ Brace Initialization: ``` const int x{17}; float const PI{2*std::acos(0.0)}; ``` ] .pull-right[ Classic Initialization: ``` const int x = 17; double const PI = 2*std::acos(0.0); ``` ] * There must be an initialisation (except when `extern`) * subsequent attempts to modify the variable * result at least in a compile time error * may also cause a run time error .N[ As the examples show, the `const` qualifier may be written to the left or to the right of the type to which it applies. ] .F[: The `const` qualifier was originally introduced with C++ but also became part of the C89 standard – hence today, 25 years later, it may well be reclaimed as a C++ addition but few people still know. Nevertheless it seems even today C++ programs make more use of this feature than C programs. ] --- template: plain header: #### `constexpr` Is To Ensure Compile Time Variables and Functions may optionally be qualified with `constexpr`: .pull-left[ ``` constexpr float PI{3.14159}; ``` ] .pull-right[ ``` constexpr float PI = 3.14159; ``` ] Qualifying a variable with `constexpr` requires the initial value can be calculated at compile time. .N[ This is not limited literal values, expressions and calling `constexpr` qualified functions with `constexpr` arguments is also supported. ] Note that the following may or may not work, depending on whether `std::acos` is `constexpr`:._[] ``` constexpr float PI{std::acos(0.0f)}; ``` .F[: The inverse cosine function is defined in header `
`. The C++11 standard does not require it is `constexpr` though it is with the GCC-Compiler (`g++`) but not with the LLVM-Compiler (`clang++`). ] ``` #include
#include
using namespace std; int main() { for (auto i : { 1, 2, 3, 4, 5, 10, 20, 100, 200, 255, 256, 127, 63, 31, 15, 7, 3, 1, 0 }) { cout << "i = " << dec << setw(5) << i << " highbit = " << dec << setw(2) << highbit(i) << " maskof = 0x" << hex << maskof(i) << endl; } } ``` --- template: plain name: arrays_and_pointers header: ### Arrays and Pointers Arrays and pointers are (sometimes) confused because for function arguments there are two different ways to express the same concept: .pull-left[ ``` void foo(short arr[]) { … } ``` ] .pull-right[ ``` void bar(short *p) { … } ``` ] Both are essentially equal as – behind the scenes – the functions will be called with the address of an `int` (arithmetic type). .N[ The difference in information conveyed in the two cases is rather about intent: * `foo` will (presumably) be called with the address of the first element of an array of `int`-s; * `bar` will be called with the address of a (presumably) single `int` or maybe even a null pointer. ] --- template: plain header: #### Indexing and Address Arithmetic When used to define some data item, arrays and pointers are different: .pull-left[ For an array storage space is set aside to store the requested number of items of the given type: ``` double data[20]; char message[1024]; ``` Individual items are typically accessed by giving their index: ``` … data[12] … … message[i] … ``` Given a pointer points to an array cell … ``` dp = &data[0]; cptr = &message[i]; ``` ] .pull-right[ For a pointer only memory space to hold an address is set aside: ``` double *dp; char *cptr; ``` Assuming an appropriate initialisation items pointed to are accessed by dereferencing: ``` … *dp … … *cptr … ``` … also address arithmetic can make sense: ``` … ++dp … … *(cptr-3) … ``` ] --- template: plain header: #### Equivalence of Indexing and Address Arithmetic There are two basic rules that make pointers and arrays equivalent when it comes to indexing and address arithmetic: * In almost any context._[] the pure name of an array decays to a pointer to the first array element, i.e. `data` and `&data[0]` are equivalent. * All pointer arithmetic is scaled with the storage size of the underlying type, making * `ptr+i` equivalent to `&ptr[i]` and * `*(ptr+i)` equivalent to `ptr[i]`. .N[ In C++ the need to use C low-level data types like arrays and pointers strongly diminishes as the standard library offers more convenient abstractions like [Container Classes](#stl_containers) and [Smart Pointers](#smart_pointers). ] .F[: Exceptions are when the name of an array is used with the `sizeof` of `&`-operator: then it will keep its array type, so `sizeof data` is the size the whole array occupies (in bytes) and `&data` is the address of the whole array, which is – of course – the same as the address of its first element but will use a different scaling for address arithmetic. ] --- template: plain header: #### Arrays Arrays belong to the C++ subset of language features assumed from C: * the size must be fixed at compile time. * unless an initialisation is supplied: ``` double data[200]; int primes_table[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 }; ``` In addition to the array data there is often some extra information stored (in separate variables) up to which element an array is filled … ``` double *filled = &data[0]; // location of next free space *filled++ = …; // idiomatic style to fill array with values ``` or how many entries exist in an initialised array: ``` const auto N = sizeof primes_table / sizeof primes_table[0]; ``` --- template: plain header: #### Arrays (2) The size information needs eventually be presented to functions that process the content of same array, as the pure array name – when used as function argument – decays to a pointer to the first array element:._[] ``` void print_primes(int table[], std::size_t count) { using namespace std; for (int i = 0; i < count; ++i) cout << table[i] << endl; } … print_primes(primes_table, N); ``` Or: ``` double sum_data(double *from, double *to) { double s = 0; while (from < to) s += *from++; return s; } … auto sum = sum_data(data, filled); ``` .F[: The C++ Standard Library provides the classes `std::array` and `std::vector` that may often be used as drop-in replacements for built-in arrays but bundle size information with the data. ] --- template: plain header: #### `const` Arrays Arrays qualified with `const` must be initialised and cannot be modified: ``` const int primes_table[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 }; ``` .N[ Initialised write protected arrays are good for pre-calculated tables of data as it can be loaded into initialised storage from the program's executable image. ] As `const` in C++ silently implies internal linkage, to share such data between translation units its declaration must also be qualified with `extern` in a header file included by all interested parties: ``` extern const int primes_table[]; ``` The initialisation from above then goes into **exactly one** translation unit. .F[: The rule *`const` implies internal linkage* allows to put the definition and initialisation of `const` data into header files without multiple definitions of the same symbol leading to linker errors. ] --- template: plain name: pointers header: #### Pointers Pointers belong to the C++ subset of language features assumed from C. Differently typed pointers are generally incompatible with each other: ``` int i; // a memory location to hold int-s int *ip = &i; // a pointer to that location float *fp = ip; // ERROR ``` .N[ If the above assignment to `fp` would pass, via `*fp` the memory location holding an `int` could be accessed as if it were a `float`.._[] ] This also defeats usage errors of multi-level pointers: ``` int **ipp = &ip; // pointer to pointer int x = 3 * *ipp; // single indirection still leaves pointer // which cannot be multiplied by an int ``` .F[: Write access could even extend beyond that and change unrelated variables, if the type that determines memory space is smaller that the type the pointer claims to point to. ] --- template: plain header: #### Typed Pointers vs. Generic Pointers (`void *`) There is a difference between C and C++ in compatibility of typed and generic pointers in, which adds more type safety: .pull-left[ In C type compatibility is both ways, from generic to typed and from typed to generic pointers: ``` int *ip; … void *p = ip; // C: OK float *fp = p; // C: OK ``` ] .pull-right[ In C++ type compatibility is one-way – from generic to typed is an error:._[] ``` int *ip; … void *p = ip; // C++: OK float *fp = p; // C++: ERROR ``` ] When at a given point the true nature of a generic pointer is known, it may be assigned back to the original typed pointer type using a cast: ``` int *ip2 = static_cast
(p); ``` .F[: Despite slightly improved type safety in C++ generic pointers cut a big hole in compile-time type checking, as anyway at some point there needs to be a cast which requires that type information is somehow embedded in the program logic. If the set of possible types is bounded at design time, class hierarchies are usually a superior alternative while for type-generic coding – e.g. in basic libraries – parametrizing types via templates provides stringent type-checking at compile-time. ] --- template: plain header: #### `const` Qualifier for Pointers .N[ Be sure to understand how `const` applies if pointers are involved! ] .pull-left[ The pointer itself is constant (and hence needs to be initialised): ``` T *const p = …; ``` ] .pull-right[ The memory location reachable via the pointer is constant: ``` T const *p; ``` Same as: ``` const T *p; ``` ] In some situations even both should be protected against modifications, the pointer and the memory to which it points:._[] ``` const T *const p = …; // a unmodifiable pointer to an // unmodifiable memory location // of type T ``` .F[: Of course `T const *const p = …;` would have the same meaning. ] --- template: plain name: references header: ### References References are more or less a different syntax for pointers. * They are declared with a leading `&` – instead of a leading `*`; * They must be initialised with some variable to which it refers, * i.e. the `&`-operator – take address – is implicitly applied to that variable. * When used, a reference is automatically dereferenced, * i.e. the `*`-operator – follow pointer, get content – is implicitly applied. .N[ Conceptually a reference is always connected to an existing entity – there is no such thing as a "Null Reference" akin to a "Null Pointer".._[] ] .F[: Nevertheless, an incautious programmer may accidentally create invalid references, e.g. like that: `void f(T *p) { …; T &r = *p; … }` (when `p` is not checked against the Null Pointer) ] --- template: plain header: #### References Viewed as Aliases A reference may also be viewed as an alias for an existing variable: ``` int i; … int &r = i; // any access to r effectively accesses i ``` .N[ The main use for this feature is for function arguments, where it minimises the syntactical overhead of explicit pointer use. ] --- template: plain header: #### References versus Pointers Two function that swap their argument values, one using pointers, one using references … .pull-left[ ``` void swap(int *p1, int *p2) { int tmp = *p1; *p1 = *p2; *p2 = tmp; } ``` ] .pull-right[ ``` void swap(int &r1, int &r2) { int tmp = r1; r1 = r2; r2 = tmp; } ``` ] … and their usage: .pull-left[ ``` int a, b; … if (a < b) swap(&a, &b); ``` ] .pull-right[ ``` int a, b; … if (a < b) swap(a, b); ``` ] --- template: plain header: #### `const`-References As references cannot be changed after initialization._[] they really are like `const` pointers – but this should not be confused with pointers to `const`: .pull-left[ ``` const int ci = 42; int i; int &ri = i; // OK const int &cri = ci; // OK const int &cri2 = i; // OK ri = …; // OK to modify cri2 = …; // ERROR int &ri2 = ci; // ERROR ``` ] .pull-right[ ``` const int ci = 42; int i; int *const ip = &i; const int *const cip = &ci; const int *const cip2 = &i; *ip = …; // OK to modify *cip2 = …; // ERROR int *ip2 = &ci; // ERROR ``` ] Any access to `ri`, `cri`, and `cri2` actually accesses the referenced variable, while applying `&` takes the address of it. Vice versa with pointers: here the plain variable usage represents the address held and applying the dereferencing operator `*` accesses the content of that memory location. .F[: To put it more clearly: a reference refers to the same storage location all of its lifetime, like a pointer that cannot be set to a different location. A different question is whether the storage location *reachable* through the reference can be modified – and this is what this page is about. ] --- template: plain header: #### Lvalue- and Rvalue-References A special property of `const` references is that they may be initialised with temporaries, typically resulting from an expression of matching type:._[] .pull-left[ ``` int a, b; … int &r = a; // OK int &r2 = a + b; // ERROR const int &r3 = a + b; // OK ``` In the last case temporary memory space must be created – for the result of the addition – and will be kept alive for the life-time of the reference. ] .pull-right[ A recent addition of C++11 are [Rvalue References]. **These can only be initialised with temporaries.** ``` int &&rr = a + b; // OK int &&rr2 = a; // ERROR int &&rr3 = a + 0; // OK ``` .N[ The main use of rvalue-references is to add move-versions of (copy-) [constructor](#move_constructor) and [assignment](#move_assignment), which will be covered later. ] ] [Rvalue References]: http://en.cppreference.com/w/cpp/language/reference .F[: Early versions of C++ allowed this for non-`const` references too but it turned out to be an error prone feature and was finally removed in later versions of C++ on which C++-98 was based. ] --- template: plain header: #### When to Use References? References should be generally preferred as function arguments for * handing over anything else besides basic types efficiently, as only an address needs to be transferred, not all of the data structure copied; * giving access to the object itself for applying modifications. .N[ Use a `const` qualified reference to protect some function argument from modifications while – behind the scenes – only handing-over its address. ] .pull-left[ [ADT]._[] to implement a counter that restarts at a given value once it had reached a given limit: ``` typedef struct { int limit; // maximum value int start; // restart value int ticks; // current value } counter; ``` ] [ADT]: http://www.youtube.com/watch?v=HcxqzYsiJ3k .pull-right[ ``` int value(const counter &c) { return c.ticks; } ``` ``` void count(counter &c) { if (++c.ticks > c.limit) c.ticks = c.start; } ``` ] .F[: ADT means abstract data type and (in the example above) comes close to what can be done with classes in C++. (In a "pure C" solution references can be easily replaced by pointers.) ] --- template: plain name: default_args header: ### Default Arguments Function arguments can have defaults, which means that the actual call can leave out such arguments.._[] ``` void say_hello(const char *greet = "hello") { cout << greet << endl; } ``` Possible calls: ``` say_hello(); say_hello("salve"); ``` The default value must be present in a prototype that declares the function and in this case the argument name is optional: ``` extern void say_hello(const char * = "hello"); ``` .F[: C++ still has no named arguments (like eg. Python) but with a due amount of boilerplate code such a mechanism may be built like described here: https://isocpp.org/wiki/faq/ctors#named-ctor-idiom ] --- template: plain name: function_ovld header: ### Function Overloading Functions may be overloaded based on their signature, which consists of * the function name and argument list * but **not** the function return type. ``` void say_hello(const char *greet) { cout << greet << endl; } void say_hello(int repeat, const char *greet) { do cout << greet << endl; while (--repeat > 0) } ``` Possible calls: ``` say_hello("hi!"); say_hello(3, "hi!"); ``` --- template: plain name: operator_ovld header: ### Operator Overloading Operators may be overloaded too, but only if at least one of operand is not a basic type.._[] ``` enum Color { Red, Blue, Green }; Color operator++(Color& c) { switch (c) { case Red: return c = Blue; case Blue: return c = Green; case Green: return c = Red; default: assert(!"never executed"); } } ``` .F[: For basic types operators are already defined – or forbidden – and that meaning cannot be changed. ] --- template: plain name: oop_intro header: ## Object Oriented Programming This chapter introduces not a minor but a substantial extensions C++ has over C, using a "limit counter" implemented as abstract data type as an example that gets gradually extended.._[] * [From `struct` to `class` ](#struct_to_class) * [Access Protection ](#access_protection) * [Constructors ](#constructors) * [Base and Derived Classes ](#base_and_derived) * [`virtual` Member Functions ](#virtual_members) .N.center[ At the low-level C++ classes still are [data `struct`-ures mapped to memory](#class_to_memory), while *Composition* and *Inheritance* are more or less nested data structures with some specific rules for type (in-) compatibility. ] .F[: There are generally two roads leading to C++, the first one, which is taken here, *step by step* turns C style programming into making use of classes and objects. The other road would radically cut off with all *"traditionally thinking"* and especially all *"old code"* and mandate a *"completely different approach"* from *"classic procedural design"*. It will often start with teaching design methodology, probably via the use of [UML Diagrams](#quick_uml). Though some proficiency with the latter will surely pay to document [OOP High-Level Designs](#class_design) it is **not at all** necessary to get substantial added value by a move from C to C++. ] --- template: plain name: struct_to_class header: ### From `struct` to `class` #### Turning Functions into Members C++ allows to define functions that take a pointer to a `struct` as (first) argument inside the class as members (aka. member functions):._[] ``` struct LimitCounter { int start; int limit; int ticks; void init(int st, int lim); void count(); void reset(); }; ``` .F[: A plain C example using just a `struct` and define the functions to access it outside can be found here: http://coliru.stacked-crooked.com/a/b2ce4b96d9725055 ] .N[ The argument to hand over the limit counter data is now implied and not named any more in the list of arguments. ] --- template: plain header: #### Defining `LimitCounter` Objects Objects (aka instances) of limit counters would be defined as follows: ``` LimitCounter lc1, lc2; ``` #### Calling Member Functions When calling the member function syntactically the object is on the left: ``` lc1.init(1, 10); lc2.init(0, 59); ``` .N[ From a low-level point of view there is no difference to a global function with an explicit pointer argument, as the address of the objects `lc1` and `lc2` is received by the member function as `this` (-pointer). ] --- template: plain header: #### Implementing Member Functions Of course, the function inside the class are just declarations that must be implemented too. This would look like follows: ``` void LimitCounter::init(int st, int lim) { this->start = this->ticks = st; this->limit = lim; } ``` As `this->` is assumed by default for any name that is not local or part of the argument list, it is usually omitted: ``` void LimitCounter::count() { if (++ticks > limit) ticks = start; } ``` --- template: plain header: #### Implementing Member Functions Inline For efficiency reasons it usually makes sense to define small member functions wtih the `inline` keyword so that their body code is inserted instead of subroutine calls at the point of their use: ``` inline void LimitCounter::reset() { ticks = start; } ``` .N[ Then a "call" to this (simple) function like `lc1.reset()` will typically result in a single `mov` instruction at the machine level, which is both, faster **and** smaller as a subroutine call! ] --- template: plain header: #### Implementing Members Inside the Class Furthermore `inline` is automatically assumed if the function is implemented inside the class to which it belongs: ``` struct LimitCounter { … void reset() { ticks = start; } … }; ``` Whether to use this or an explicit `inline` with an implementation outside of the class is largely a matter of taste. .N[ When classes are – as usual – defined in separate header files, `inline` implementations **must go to the header file** because the compiler needs to know them when it emits the code for a "function call". ] --- template: plain name: access_protection header: ### Access Protection It is usually considered "good style" if a user defined type protects its data members from direct access but adds accessors instead: .pull-left[ ``` class LimitCounter { int start; int limit; int ticks; public: … int value() const { return ticks; } void count() { if (++ticks > limit) ticks = start; } }; ``` ] .pull-right[ The `const` qualifier for the member function – between the closing parenthesis and the opening curly brace – allows to call this function for non-modifiable `LimitCounter` instances: ``` void foo(const LimitCounter &c) { cout << c.value(); // OK c.count(); // ERROR } ``` ] .F[: There are several common naming schemes for such accessors. A project should probably chose one of this schemes and apply it consistently for all its classes. ] --- template: plain name: protection_levels header: #### Levels of Access Protection There are several protection levels which can be interspersed as often as necessary with the members in `struct`- or `class`-blocks like labels:._[] * `public:` – accessible from outside * `private:` – accessible for member functions only * `protected:` – accessible for member functions and [derived classes](#base_and_derived) (covered later) .pull-left[ ``` class Mine { public: … protected: … private: … }; ``` ] .pull-right[ ``` class Other { … public: … private: … public: … }; ``` ] .F[: Despite the similar syntax these are **not** labels, as the names are reserved keywords and the syntax limits their use to `struct` and `class` blocks. ] --- template: plain name: struct_vs_class header: #### `struct` vs. `class` The only difference between `class` and `struct` is that the latter starts by default with a `public:` section and the former with a `private:` section. .pull-left[ ``` struct Mine { private: … public: … }; ``` ``` struct Other { … private: … }; ``` ] .pull-right[ ``` class Mine { … public: … }; ``` ``` class Other { public: … private: … }; ``` ] --- template: plain name: constructors header: ### Constructors Classes may have Constructors which will – if they exist – be called automatically when an instance of the given class comes into existence: ``` class LimitCounter { … public: LimitCounter(int st, int lim) { start = ticks = st; limit = lim; } }; ``` Constructor arguments must now be supplied with the definition of instances: ``` LimitCounter lc1(1, 10), lc2(0, 59); ``` --- template: plain name: constructors header: #### Constructors: Member Initialisation Lists There is an alternative syntax to initialise data members, the *MI-list* (short for member initialisation list). Its use is mandatory when constants and references are initialised, but they may optionally be used always: ``` class LimitCounter { const int start; const int limit; int ticks; public: LimitCounter(int st, int lim) : start(st), limit(lim), ticks(st) {} … } ``` .N[ It is not at all unusual that – when the MI-list is used for all members – the constructor body stays empty.._[] ] .F[: Of course, to maximize consistency by re-use, it might alternatively be considered in the above example to initialise `ticks` with a call to `reset()` from inside the constructor body. ] --- template: plain name: destructor header: ### Destructors The destructor is the complementary operation to the constructor. It has the same name as the class prefixed with `~` and will automatically be called if an instance of the given class ceases to exist. There is no need for a destructor in the `LimitCounter` class. .N[ An example for a class that actually needs a destructor follows in the section on [Dynamic Memory Allocation](#dynamic_memory). ] --- template: plain name: static_members header: ### Static Members Member variables and functions may be ´static`, which means they exist only once (for the class), not for every instance. As there is no need for static members in the `LimitCounter` example, the following shows how a class could count the number of its instances:._[] .pull-left[ In the header defining the class: ``` // file: Foo.h class Foo { … static int instances; public: Foo() { ++instances; } ~Foo() { --instances; } … }; ``` ] .pull-right[ In exactly one translation unit: ``` // file: Foo.cpp … int Foo::instances = 0; … ``` ] .F[: This example is actually incomplete as it omits the copy constructor (not covered so far) which would also need to be implemented as it must increment the `instances` count too. ] --- template: plain name: base_and_derived header: ### Base and Derived Classes Classes may be derived from other classes if they only want to extend some existing functionality. .N[ The following defines a new class `OverflowCounter` which adds to a `LimitCounter` the capability that in case of an overflow it increments a connected counter. ] The approach is shown step by step on the next pages. --- template: plain name: base_and_derived header: #### Derive from a Base Class The syntax names the base class after the derived: ``` class OverflowCounter : public LimitCounter { … }; ``` The use of `public` here implies that an `OverflowCounter` can always replace a `LimitCounter`, which will become important soon.._[] Technically the principle of substitutibility mean there is an automatic type conversion from the derived to the base class. .N[ According to Barbara Liskov, who first formulated that principle, it is also named [Liskov Subsitution Principle] or LSP in short. ] [Liskov Substitution Principle]: https://en.wikipedia.org/wiki/Liskov_substitution_principle .F[: If this kind of substitutability is not desirable, a `private` base class should be used. ] --- template: plain header: #### Implementation and Consequences of the LSP If the data members of the base class are put first into the data structure of derived class, implementing the LSP effectively is a no-op at run-time: * A pointer to a derived class also points to the members of the base class (as such have the same offset in the structure) * Same for a reference to the base class vs. a reference of the derived class, as behind the scenes references are implemented as pointers. * When a value of the derived class initializes or is assigned to a value of base class, only the base class members are actually assigned. .N[ By default all public members (data and functions) of the base class are visible as members of the derived class. ] Typically, but not necessarily, a derived class **extends** the base class with more member data and functions, beyond what was "inherited" from the derived class. --- template: plain header: #### Overwriting Inherited Implementations A derived class may **override** implementations of member functions. .N[ Usually derived classes **do not** something arbitrary and completely different! Rather they will **extend** the inherited implementation.._[] ] In most practical implementations this means the base class needs to "cooperate" to a degree, e.g. * accept (slightly) [suboptimal run-time behavior](#suboptimal_reuse); * [reduce protection](#reduce_protection) of members wrt. to derived classes (by making them `protected:` instead of `private`); * [give hints](#giving_hints) to derived classes beyond the needs of its direct clients; * chose to use a design with [pre-planned extension points](#extension_points) instead of overwriting a global function (aka. [Non-Virtual Interface] or NVI Idiom). [Non-Virtual Interface]: https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-Virtual_Interface .F[: To stay within the limits in which a derived class may "alter" the base class implementation, be sure to understand the principle or *Pre-Conditions* and *Post-Conditions* as outlined here: https://isocpp.org/wiki/faq/proper-inheritance ] --- template: plain header: #### Add Member Data A derived class is always free to add member variables and functions._[] to what is inherited from its base class. .N[ The `OverflowCounter` needs to add a reference to the next stage (which will count on overflow). ] ``` class OverflowCounter : public LimitCounter { LimitCounter &next; }; ``` Note that * an object of type `OverflowCounter` **contains all the data** that belongs to a `LimitCOunter` * but the `LimitCounter` object reachable via `next` **is a different instance** than the one contained because of its `LimitCounter` base class. .F[: So far in this example no member functions are added. ] --- template: plain header: #### Add A Constructor To initialise data members added, a derived class usually needs its own constructor. ``` class OverflowCounter : public LimitCounter { LimitCounter &next; public: OverflowCounter(int st, int lim, LimitCounter &n) : LimitCounter(st, lim), next(n) {} … }; ``` .N[ The constructor forwards its first two arguments to the constructors of its `LimitCounter` base class and uses the third (reference) argument to initialise `next`.._[] ] .F[: Clearly there is the need for different constructor when the derived class adds data members, as such need to be initialized too. If the base class has "a whole bunch of constructors" which all make sense for the derived class too this can turn into an substantial of work. In C++11 several new features come to relieve, like *Direct Member Initialisation*, *Constructor Inheritance and Delegation*, and *Variadic Templates* combined with *Perfect Forwarding*. **These are only demonstrated on demand.** ] --- template: plain header: #### Add Another Member Function Of course, an `OverflowCounter` has a slightly different way to count compared to a `LimitCounter`: ``` class OverflowCounter : public LimitCounter { LimitCounter &next; public: … void count(); }; ``` .N[ So far this only declares the **intent** to implement `count()` **differently** from the inherited implementation. ] --- template: plain name: suboptimal_reuse header: #### OverflowCounter Overwriting `count` Now it needs to replace the inherited `count()` member function.._[] ``` void OverflowCounter::count() { // overriding inherited count int old = value(); // getting inherited value() LimitCounter::count(); // calling inherited count() // (qualification necessary here // to avoid recursive call !!) if (value() < old) next.count(); // calling next stage `count()` } ``` .N[ Looking closely shows that semantically this also **extends** the implementation inherited. ] While this solution works, it looks (and is) clumsy, but any improvement would mean that the base class needs to take into account the needs of its derived class. .F[: The technical term here is "overriding", though in German literature this is often translated with "überscheiben" (= overwriting). ] --- template: plain name: reduce_protection header: #### Reducing Protection of Members One possibility for a more elegant way would be a reduced protection of its `start` or `limit` data member. The following makes it accessible for a derived class: .pull-left[ ``` class LimitCounter { … protected: const int start; … } ``` The solution still looks clumsy: ``` void OverflowCounter::count() { LimitCounter::count(); if (value() == start) next.count(); } ``` ] .pull-right[ ``` class LimitCounter { … protected: … const int limit; } ``` Or alternatively: ``` void OverflowCounter::count() { if (value() == limit) next.count(); LimitCounter::count(); } ``` ] .F[: The full example can be found here: http://coliru.stacked-crooked.com/a/e69477f058f431ab ] --- template: plain name: giving_hints header: #### Giving Hints to From Base Classes Another possibility were to have the base class give a hint to the derived class. In the scenario used as example so far this hint could be the return value of the `count` member function: ``` bool LimitCounter::count() { if (++ticks > limit) { ticks = start; return true; } return false; } ``` --- template: plain header: #### Using Hints in Derived Classes The derived class would now make use of this hint … ``` bool OverflowCounter::count() { if (LimitCounter::count()) { next.count(); return true; } return false; } ``` … but assuming it may in turn be used as base class from some derived class that needs this hint, it has to forward it accordingly via its own return value. .N.center[ Still not a really nice solution! ] .F[: The full example can be found here: http://coliru.stacked-crooked.com/a/57e620c867233fed ] --- template: plain header: #### A Chain of Counters Now consider the case that a chain of counters is build, e.g. as display of a clock: ``` int main() { LimitCounter hours(0, 23); OverflowCounter minutes(0, 59, hours); OverflowCounter seconds(0, 59, minutes); … // probably in a loop ... seconds.count(); … } ``` The fact that the "middle" counter (`minutes`) can be used as constructor argument for another `OverflowCounter` is worth mentioning here: .N[ It is only possible because the `OverflowCounter` has the `LimitCounter` as `public` base class and therefore – as an instance of the derived class – the former is always accepted as a substitute for the latter. ] .F[: The full example can be found here: http://coliru.stacked-crooked.com/a/59c721aa3218086b ] --- template: plain name: virtual_members header: ### `virtual` Member Functions .W[ Actually the chained counter example will not work unless `count()` is declared `virtual` in the base class `LimitCounter`. ] In C++ member functions are not `virtual` by default as * [**calling** a `virtual` Member function](#virtual_mfn_call) comes at the cost of an indirection; * a `virtual` member function typically can not be expanded inline, * leading to a substantial performance penalty on modern hardware with fast local instruction caching and pre-fetch mechanisms, * especially for small functions which otherwise are often desirable for modularity and other reasons. .F[: Another examples for the use of virtual member functions follows in the next section, applying an idiom known as *Non Virtual Interface*. Besides that there is an example in the optional Info-Graphics. It covers [type-dependant branching](#practical_rtti) and also explains how this usually relates to a `virtual` member functions "missing per design". ] --- template: plain header: #### Compile-Time vs. Run-Time Types For a slightly more general point of view with respect to `virtual` member functions the difference between 'Compile-Time Type` and `Run-Time Type` needs to be understood.._[] * The compile-time of member `next` in `OverflowCounter` is `LimitCounter`. .N[ Calling a **regular** (non-`virtual`) member function on `next` will resolve to the implementation provided by class **`LimitCounter`**. ] * At run-time the type may * actually be a `LimitCounter` (as it is for `minutes`), or * or anything derived from it (as it is for `seconds`). .N[ Calling a **`virtual`** member function on `next` will resolve to the implementation provided by class **`OverflowCounter`**. ] .F[: That difference also shows up in [Run-Time Type Identification] (or RTTI in short) which since C++98 can be done with either `typeid` or `dynamic_cast` and is only covered here on demand. ] --- template: plain name: extension_points header: #### Add Extension Points in the Base Class A far superior solution is possible if the base class (`LimitCounter`) had anticipated the needs of its derived classes and added an extension point: ``` class LimitCounter { … virtual void overflow() {} public: void count(); … }; ``` The base class does nothing in this case – except calling a function that returns immediately: ``` void LimitCounter::count() { if (++ticks > limit) { overflow(); ticks = start; } }; ``` --- template: plain header: #### Hooking Code to Extension Points from Derived Classes This would be the **full** code of the class `OverflowCounter` then: ``` class OverflowCounter : public LimitCounter { LimitCounter &next; virtual void overflow() override { next.count(); } public: OverflowCounter(int st, int lim, LimitCounter &n) : LimitCounter(st, lim), next(n) {} }; ``` Especially now there is no need any more to override the `count()` member function inherited from `LimitCounter`. .N.center[ Instead the function for the extension point is overridden.._[] ] .F[: An advantage achieved thereby is that it cannot be forgotten to call the inherited function from the overwritten one. ] --- template: plain header: #### The "Open-Close Principle" * *Extension Points*s as shown so far can also be viewed as an application of the * *Template Method* design pattern outlined by the [GoF-Book]. .N[ From a more general point of view this is the [Open-Closed Principle]._[] as defined 30 years ago by Bertrand Meyer and still alive today …._[] ] [Open-Closed Principle]: https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle It is illustrated in an optional [Info-Graphic](#two_open_closed) which shows * an approach with C++-Templates * besides the typical approach with `virtual` member functions. (The latter according to the *Template Method Pattern*, covered in more depth by books dealing with *OOP Design Patterns*, like the [GoF-Book].) [GoF-Book]: https://en.wikipedia.org/wiki/Design_Patterns .F[: … as the "O" in the set of "SOLID" Principles (with "L" in representing the Liskov Substitution Principle.) ] --- template: plain name: alternative_design header: ### Alternative Design An alternative design could use **one class only** that is similar to the `OverflowCounter` used so far. The difference is that it connects the next counter – the one which counts on overflow – via the member `pnext` which is a pointer and is a null pointer if there is no next counter attached. ``` class OverflowCounter { const int start; const int limit; int ticks; OverflowCounter *pnext; public: OverflowCounter(int st, int lim, OverflowCounter *pn = 0) : start(st), limit(lim), pnext(pn) {} void count() { if ((++ticks > limit) && (pnext != 0)) pnext->count(); } … }; ``` --- template: plain name: exception_intro header: ## Introduction to Exceptions [Basically exceptions are](#exception_basics) * non-local branches, * backwards in the call tree, * to a `catch`-block in a function that has not yet returned, * and its preceding `try`-block active.._[] .F[: Some more specific information on exceptions is provided in an separate [Info-Graphic](#exception_specifics). ] --- template: plain name: exception_throw header: ### Throwing Exceptions A `throw` statement is used to terminate current processing. It needs to be followed by an expression, which has a type and a value.._[] * Sometimes basic types are used as exceptions, * but typically a class (or small class hierarchy) is defined for that purpose. ``` … throw 42; // integer thrown … throw "let me out here!"; // const char* thrown … throw ErrorX(12, 15); // instance of class ErrorX thrown ``` .F[: A `throw`-statement usually is only conditionally, i.e. when a certain condition is detected that prevents following the ordinary flow of control any further. ] --- template: plain name: exception_throw header: ### Catching Exceptions The `catch` block defines a parameter which acts much like a function argument.._[] * Actually it receives the value of the expression thrown, * therefore its type must be compatible with the type thrown. Catch 42 (and other int-s): ``` try { … } catch (int x) { … } ``` Catch `"let me out here"` (and other C-style strings): ``` try { … } catch (const char* txt) { … } ``` Catch instances of class `ErrorX` and classes publicly derived from `ErrorX`: ``` try { … } catch (ErrorX &ex) { … } ``` .F[: The examples shown here are written into a single line only to safe some space. Usually the `try`- and `catch`-block would be formatted to extend over several lines, as is necessary from its contents. The examples shown here are written into a single line only to safe some space. Usually the `try`- and `catch`-block would be formatted to extend over several lines, as is necessary from its contents. ] --- template: plain name: configurable_exceptions header: ### Configurable Exceptions A strategy to make the throwing of exceptions configurable could be not to throw directly but through a virtual member function:._[] ``` struct ErrorX { ErrorX(int, int); }; … class Mine { virtual void error_X(int, int) = 0; public: void do_it() { … if (…) error_X(12, 15); … } }; ``` .F[: As shown so far `Mine` is an abstract class which can only be used as base class for derived classes like those shown on the next pages. Of course one of these following classes could also serve as default implementation. ] --- template: plain header: ### Configurable Exceptions (2) Instances of the following class actually throw when the error happens: ``` class ThrowingMine : public Mine { virtual void error_X(int a, int b) { throw ErrorX(a, b); } } ``` Instances of this class ignore the problem: ``` class NonThrowingMine : public Mine { virtual void error_X(int, int) { /*empty*/ } } ```