Objektorientiert Programmieren mit C++


Thematik
C++ ist eine Objekt-Orientierte Programmiersprache. 
Sie ermöglicht die Entwicklung anspruchsvoller Software.

Für wen ?
Programmierer, die C++ unter Verwendung
Objektorientierter Konzepte einsetzen wollen.

Das sollten Sie mitbringen !
Es sind keine Vorkenntnisse erforderlich

Was bringt es Ihnen?
Praxisorientiertes Umsetzen objektorientierter Techniken.
Kompetenter Einsatz der C++ Syntax .

Dauer  5 Tage


Inhalte

C++ Einführung

Wie ist C++ entstanden ?     
Was leistet die Sprache ?
Für welche Art von Programmen ist sie ausgelegt ?
Was bringt mir das für mein Projekt ?
Die Nähe zu C


Features von C++ auch ohne OOP

Der neue Datentyp "bool".
Referenzen sind initialisierte Zeiger. 
Referenzen in Aufrufparametern vermeiden Zeiger.  
Funktions-Überladung: mehrere Funktionen mit gleichem Namen, 
unterschieden wird durch die Parameter.
Einfacheres Allokieren und Freigeben von Heap-Speicher.
IO mit einer komfortablen Klassenbibliothek.


Grundlagen der Objekt-Orientierten Programmierung

Überbegriffe bilden, Klassifizieren tun wir ständig,
warum nicht auch beim Programmieren.

Die Klasse ist der Bauplan.
Das Programm arbeitet letztendlich mit Objekten.
Wie finde ich Klassen ? Durch Abstrahieren.

Warum ist Verkapselung so wichtig ?
Wie werde ich darin von C++ unterstützt ?

Code wieder verwenden durch Vererbung.
Vererbung stellt eine "Ist" - Beziehung dar.
Es gibt auch die "Hat" - Beziehung, die Aggregation. 

Eine Klasse kann von mehreren Klassen erben.

Über eine Objekt-Liste iterieren und je nach Objekt-Typ
reagieren - Der Polymorphismus, ein Highlight von OOP.

Abstrakte Klassen liefern Code, 
fordern aber auch eine Implementierung.


Die Klasse in C++

Wie wird eine Klasse in C++ geschrieben ?

Variable sollten immer privat sein.
Dafür schreibe wir dann public Zugriffsfunktonen.

Klassen haben Methoden.

Hat jedes Objekt seinen eigenen Code ?
Wie handelt C++ den Zugriff auf die Daten ? Über den "this - Zeiger".

Wenn alle Bankkonten den gleichen Zinssatz haben,
wäre eine Objektvariable Verschwendung - Die statische Variable 
gehört der Klasse, nicht dem Objekt 


Wie werden Objekte erstellt und entsorgt?

Für die "Konstruktion" eines Objektes gibt es den Konstruktor.
Den Standard Konstruktor bekommen wir vom Compiler zur Verfügung gestellt, aber
auch nicht immer.

Oft ist es besser diesen doch selbst zu schreiben.
Objekte sollten eigentlich nicht mit Standard Konstruktoren initialisiert werden.

Objekte sollten auf verschiedene Arten initialisiert werden können, daher schreiben
wir mehrere Konstruktoren

Konstruktoren heißen wie die Klassen, die Überladung von Methoden ist
hier also zwingend notwendig.
Schreiben wir Konstruktoren mit Parametern, bekommen wir keinen Standard
Konstruktor mehr zur Verfügung gestellt, hier hilft nur selber schreiben, sofern wir ihn
brauchen.

In C++ können Methoden vorbelegte Parameter haben, das gilt auch für 
Konstruktoren, sind alle Parameter vorbelegt, dann haben wir wieder einen 
Standard-Konstuktor.

Objekte können über andere Objekte initialisiert werden, wir erhalten eine
Kopie. C++ kopiert Elementweise, auch Zeiger, diese zeigen auf den gleichen 
Heap Bereich. Das geht gut, bis die Objekte durch einen vorhandenen Destruktor
gelöscht werden, ggf. wird der gleiche Bereich 2 mal freigegeben, dann kracht es. 

Die Lösung : Der Kopier-Konstruktor.

Es gibt auch statische und globale Objekte.

Was muss getan werden, wenn Objekte selbst Objekte enthalten ?
Das äussere Objekt ruft das innere Objekt über eine Initalisierungsliste auf. 

Wie werden Objekte entsorgt ? Über Destruktoren.
Wie ist die Aufrufreihenfolge ?


Arrays von Objekten

Es gibt 2 Möglichkeiten Arrays von Objekten anzulegen :

Ohne gleichzeitige Initialisierung,
dann ist ein Default Konstruktor erforderlich.

Mit Initialisierung,
dann sind beliebige Konstruktoren möglich.


Exceptions

Warum Exceptions ? 

Mehrfache Catch Blöcke : zuerst spezifische Exceptions dann allgemeine 
Exceptions abfangen.

Bitte Exceptions auch nur für Ausnahmen verwenden, nicht für den 
normalen Programmablauf !

Mit "throw" Exceptions auslösen.
  


Die Vererbung

Vererbung dient zur Wiederverwendung von Code.
Abgeleite Klassen stehen in einer "Ist"- Beziehung zur Basisklasse. 
Es gibt auch die "Hat" - Beziehung, die Aggregation. 

Auf "Protected" - Member kann ungehindert von abgeleiteten Klassen zugegriffen
werden, andere Klassen haben keinen Zugriff.  Der "protected" Zugriff wirkt für
abgeleitete Klassen wie "public", ansonsten wie "private".

Hat die Basis-Klasse einen Default Konstruktor, wird dieser automatisch von
der abgeleiteten Klasse aufgerufen.
Vorsicht:
Hat die Basis Klasse einen Konstruktor mit Parametern, muss der Aufruf des
Basisklassen Konstruktors im Konstruktor der abgeleiteten Klasse in einer
Initialisierungs-Liste erfolgen.
Wie ist der Aufruf und wie wird die Liste übergeben ?

Wie ist die Aufrufreihenfolge der Konstruktoren

Destruktoren entsorgen die Objekte


Überladene Operatoren

In C++ können Operatoren überladen werden. Sie werden wie eine Funktion deklariert
und können dann in einem Ausdruck verwendet werden. Ohne diese Möglichkeit wären
Ausdrücke wesentlich komplexer. Durch die Operator - Überladung ergibt sich eine
intuitive Schreibweise.

Welche Operatoren sollten  überladen werden. ?

Ganz wichtig ist ein eigener Zuweisungsoperator, bei Klassen, die Zeiger enthalten.
Hier treten die gleichen Probleme wie bei der Objekt Erstellung auf.
Wie schaut die Implementierung aus ?

Welche Operatoren eignen sich noch zum Überschreiben ?


Der Polymorphismus

Die Basisklasse enthält eine Minimalimplementierung,
diese kann auch nur aus { } bestehen.

Die abgeleiteten Klassen haben die Möglichkeit eine eigene geeignetere 
Implementierung anzugeben, ist sie vorhanden, wird sie verwendet.

Wie wird der Polymorphismus in C++ realisiert ?
Jede Klasse mit virtuellen Funktionen erhält vom Compiler eine Tabelle zugeteilt.
Hier werden die Adressen der virtuellen Funktionen eingetragen, es handelt sich also
um eine Sprung Tabelle, die VMT (Virtuelle Methoden T abelle). Jedes Objekt dieser
Klasse erhält in den ersten 4 Bytes seines Datenlayouts einen "hidden" Zeiger
auf die VMT.

Wie wird die richtige Methode gefunden ?
Wird die virtuelle Methode über ein Objekt aufgerufen, weiß der Compiler welche
Funktion gemeint ist, er bindet zur Compile-Time, das ist aber keine Polymorphismus.

Wird eine Methode über einen Basisklassenzeiger aufgerufen, steht zur Compile-Time
nicht fest welche Methode ausgeführt werden soll, je nach Typ im Zeiger wird ja ggf. eine
andere Funktion ausgeführt, er muss Code erzeugen um die Funktionsadresse zur
Laufzeit zu bestimmen.
Wie findet die Anwendung zur Laufzeit die richtige Funktionsadresse ?
Im referenzierten Objekt wird über den Zeiger _vfptr die Methoden Tabelle geladen
und über einen Index die entsprechende Funktionsadresse ermittelt , es wird also spät gebunden.


Friends

Auch in OOP gibt es Freunde. 
Diese Freundschaften werden von einer Klasse an Funktionen oder an andere
Klassen vergeben.

Die Freunde dürfen dann auf die privaten Member der Klasse zugreifen.
Freundschaften werden jedoch nicht vererbt.


Mehrfachvererbung

C++ ist eine der wenigen Objektorientierten Sprachen, die es erlauben von mehreren 
Basisklassen Code zu erben.
Wie  wird diese  Mehrfachvererbung realisiert ?

Wie ist der Objektaufbau ?

Vorteil der "Multiple Inheritance" : intuitiver Zugriff.
Nachteil: Ambiguity (Mehrdeutigkeit) bei gleichnamigen Methoden aus verschiedenen
Basis Klassen.

Wann brauche ich die virtuelle Vererbung ?


Templates

Es gibt 2 Arten von Templates
Klassen-Templates und Funktions-Templates

Warum Templates ?
Beispiel Funktion Max: Sie gibt die größere Zahl von 2 Zahlen zurück. Für jeden
Max -Typ (short, int oder long) muss eine neue Max-Funktion erstellt werden.
Es wären 3 Max-Funktionen zu schreiben.

Die Lösung mit Templates:
Es wird eine allgemeine Funktion geschrieben, die nicht den Typ short, int oder long
enthält sondern einen allgemeinen Typ z.B. "T". Beim Aufruf der Funktion generiert
der Compiler zunächst den  "richtigen Code" mit dem Typ, der beim Aufruf verwendet wurde.

Bei 3 Aufrufen mit unterschiedlichen Typen wird natürlich auch 3 mal unterschiedlicher
Code erzeugt, wir mussten ihn aber nur einmal schreiben.

Wird die Max-Funktion mit dem gleichen Typ noch einmal aufgerufen, dann wird der
schon vorhandene Code verwendet.

Beide Argumente müssen den gleichen Datentyp haben. Es erfolgt keinerlei
Typanpassung. Dafür gibt es mehrere Lösungen: casten, Template Argument
explizit angeben, Wrapper erstellen

Das Template liefert in seiner ursprünglichen Version nicht immer das erwartete
Ergebnis
zB. Wird das Template Max für 2 Strings verwendet, so wird der Zeiger mit der
höheren Adresse zurückgegeben.
Lösung durch Spezialisierungen von Template Funktionen.