1. Von der Idee zur Software 2. Funktionen und Datenstrukturen Lehrbuch: 4.3 3. Organisation des Quellcodes 4. Werte- und Referenzsemantik



Ähnliche Dokumente
Die verschiedenen Programmierparadigmen von C++ Software-Technik: Vom Programmierer zur erfolgreichen

Wintersemester Maschinenbau und Kunststofftechnik. Informatik. Tobias Wolf Seite 1 von 22

Objektorientierte Programmierung. Kapitel 12: Interfaces

Verhindert, dass eine Methode überschrieben wird. public final int holekontostand() {...} public final class Girokonto extends Konto {...

Prinzipien Objektorientierter Programmierung

Programmieren in Java

Einführung in die Java- Programmierung

Vererbung & Schnittstellen in C#

Vorkurs C++ Programmierung

Software Engineering Klassendiagramme Assoziationen

Objektorientierte Programmierung

5. Abstrakte Klassen. Beispiel (3) Abstrakte Klasse. Beispiel (2) Angenommen, wir wollen die folgende Klassenhierarchie implementieren:

Objektorientierte Programmierung

3 Objektorientierte Konzepte in Java

II. Grundlagen der Programmierung. 9. Datenstrukturen. Daten zusammenfassen. In Java (Forts.): In Java:

1. Grundlegende Eigenscha5en 2. Redefini+on 3. Polymophie 4. Mehrfachvererbung

Einführung in die Programmierung

5. Abstrakte Klassen

SEP 114. Design by Contract

Objektorientierte Programmierung OOP

Prof. Dr. Uwe Schmidt. 21. August Aufgaben zur Klausur Objektorientierte Programmierung im SS 2007 (IA 252)

Klassendiagramm. Kurzer Überblick über UML - Stand BlaBla

7. Objektorientierte Softwareentwicklung/3. Informatik II für Verkehrsingenieure

Session Beans & Servlet Integration. Ralf Gitzel ralf_gitzel@hotmail.de

Arbeiten mit UMLed und Delphi

Java: Vererbung. Teil 3: super()

AGROPLUS Buchhaltung. Daten-Server und Sicherheitskopie. Version vom b

Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen

Die Bedeutung abstrakter Datentypen in der objektorientierten Programmierung. Klaus Kusche, September 2014

Klassenbeziehungen & Vererbung

C++ - Einführung in die Programmiersprache Polymorphismus und Vererbung. Eltern

Fachdidaktik der Informatik Jörg Depner, Kathrin Gaißer

.NET Code schützen. Projekt.NET. Version 1.0

Grundlagen von Python

Datensicherung. Beschreibung der Datensicherung

Einführung in die objektorientierte Programmierung mit Java. Klausur am 19. Oktober 2005

Das Typsystem von Scala. L. Piepmeyer: Funktionale Programmierung - Das Typsystem von Scala

Java Einführung Umsetzung von Beziehungen zwischen Klassen. Kapitel 7

GetName(), GetName(), GetGeschlecht() und AelterWerden().

Software Engineering Interaktionsdiagramme

Einführung in die Programmierung

1 Mathematische Grundlagen

Client-Server-Beziehungen

Lineargleichungssysteme: Additions-/ Subtraktionsverfahren

Objektorientierte Programmierung mit C++ Zusammenfassung der wichtigsten Topics rund um die objektorientierte Programmierung mit C++11

Übungen zu Einführung in die Informatik: Programmierung und Software-Entwicklung: Lösungsvorschlag

Bauteilattribute als Sachdaten anzeigen

Eva Douma: Die Vorteile und Nachteile der Ökonomisierung in der Sozialen Arbeit

Drei-Schichten-Architektur. Informatik B - Objektorientierte Programmierung in Java. Vorlesung 16: 3-Schichten-Architektur 1 Fachkonzept - GUI

Folge 19 - Bäume Binärbäume - Allgemeines. Grundlagen: Ulrich Helmich: Informatik 2 mit BlueJ - Ein Kurs für die Stufe 12

Installation und Inbetriebnahme von Microsoft Visual C Express

Kapitel 6. Vererbung

Abituraufgabe zur Stochastik, Hessen 2009, Grundkurs (TR)

Virtueller Seminarordner Anleitung für die Dozentinnen und Dozenten

13 OOP MIT DELPHI. Records und Klassen Ein Vergleich

Java Kurs für Anfänger Einheit 4 Klassen und Objekte

Schnittstelle DIGI-Zeiterfassung

Netzwerkeinstellungen unter Mac OS X

Anwendungsbeispiele Buchhaltung

4 Vererbung, Polymorphie

M. Graefenhan Übungen zu C. Blatt 3. Musterlösung

Von der UML nach C++

In 12 Schritten zum mobilen PC mit Paragon Drive Copy 11 und Microsoft Windows Virtual PC

Unterprogramme. Funktionen. Bedeutung von Funktionen in C++ Definition einer Funktion. Definition einer Prozedur

3. Konzepte der objektorientierten Programmierung

Algorithmen und Datenstrukturen

Diplomarbeit. Konzeption und Implementierung einer automatisierten Testumgebung. Thomas Wehrspann. 10. Dezember 2008

Durchführung der Datenübernahme nach Reisekosten 2011

C/C++-Programmierung

Anwendungsbeispiele Buchhaltung

Kapitel 6. Vererbung

EinfÅhrung in die objektorientiere Programmierung (OOP) unter Delphi 6.0. EDV Kurs 13/2

Zum Abschluss wird gezeigt, wie aus einem C++ Quell-Programm ein ausführbares Programm erzeugt wird. 1. Installation von NetBeans...

Software Engineering Klassendiagramme Einführung

PRAXISBUTLER ANPASSUNG DER VORLAGEN

Step by Step Webserver unter Windows Server von Christian Bartl

In 15 einfachen Schritten zum mobilen PC mit Paragon Drive Copy 10 und Microsoft Windows Virtual PC

Vgl. Oestereich Kap 2.7 Seiten

Das Leitbild vom Verein WIR

Tevalo Handbuch v 1.1 vom

Planung für Organisation und Technik

Übungen zu C++ Kapitel 1

Outlook Vorlagen/Templates

Mediumwechsel - VR-NetWorld Software

Folge 18 - Vererbung

Kapitalerhöhung - Verbuchung

Ziel, Inhalt. Programmieren in C++ Wir lernen wie man Funktionen oder Klassen einmal schreibt, so dass sie für verschiedene Datentypen verwendbar sind

OO Softwareentwicklung

Name: Klausur Programmierkonzepte SS 2011

Assoziation und Aggregation

Klausur in Programmieren

Klausur zur Einführung in die objektorientierte Programmierung mit Java

Alltagsnotizen eines Softwareentwicklers

Leichte-Sprache-Bilder

Anwendungsbeispiele. Neuerungen in den s. Webling ist ein Produkt der Firma:

Dokumentation für die software für zahnärzte der procedia GmbH Onlinedokumentation

Objektorientiertes JavaScript

Kapitel 6. Vererbung

Fakultät Angewandte Informatik Lehrprofessur für Informatik

Hilfe zur Urlaubsplanung und Zeiterfassung

Transkript:

Software-Technik: Vom Programmierer zur erfolgreichen Software-Technik: Vom Programmierer zur erfolgreichen 1. Von der Idee zur Software 2. Funktionen und Datenstrukturen Lehrbuch: 4.3 3. Organisation des Quellcodes 4. Werte- und Referenzsemantik Kompendium, 4. Auflage: 9.2 5. Entwurf von Algorithmen 6. Fehlersuche und behandlung 7. Software-Entwicklung im Team 8. Abstrakte Datentypen: Einheit von Daten und Funktionalität 9. Vielgestaltigkeit (Polymorphie) 10. Entwurfsprinzipien für Software Kompendium, 3. Auflage: 8.10, 9.3 Anhang A: Die Familie der C-Sprachen Anhang B: Grundlagen der C++ und der Java-Programmierung 9 Vielgestaltigkeit (Polymorphie) 9.1 Statische Bindung 9.2 Dynamische Bindung 9.2.1 Polymorphie selbst gemacht 9.2.2 Automatische Polymorphie 9.2.3 Polymorphie ganz konkret 9.3 Vererbung 9.3.1 Code in mehreren Klassen gemeinsam nutzen 9.3.5 Die Verwendung von Vererbung 9.4 Zusammenfassung Folien mit gelben Punkten am oberen rechten Rand sind weniger wichtiger für das Verständnis der nachfolgenden Kapitel. Folien seit erster Vorstellung in Vorlesung (10.5.07) angepasst V 4.01; Hon. Prof. Helmke 1 V 4.01; Hon. Prof. Helmke 2 Statisches Binden Schnittstelle Einheitliche Schnittstellen erleichtern die Austauschbarkeit von Moduln und Datentypen Ist es denn so wahrscheinlich, dass wir mehrere Implementierungen vorliegen haben, die wir hinter der gleichen Schnittstelle verstecken wollen? Paradebeispiel sind die Container (Listen, Bäumen, Hash-Tabellen ), die alle über die gleiche Schnittstelle angesprochen werden Mul Div Set Complex Copy Real Sub Imag Add Es interessiert erst mal nicht, wie die Complex x Komplexe = Set(1.1, Zahl 2.2), intern y, (durch z; Attribute) implementiert wird (Hülle/Ei-Modell). Es kann durchaus mehrere Implementierungen y = Copy(x); (auch gleichzeitig) geben. z = Add(x, y); V 4.01; Hon. Prof. Helmke 3 V 4.01; Hon. Prof. Helmke 4

Software-Technik: Vom Programmierer zur erfolgreichen Polymorphie 9 Vielgestaltigkeit (Polymorphie) 9.1 Statische Bindung 9.2 Dynamische Bindung 9.2.1 Polymorphie selbst gemacht 9.2.2 Automatische Polymorphie 9.2.3 Polymorphie ganz konkret 9.3 Vererbung 9.3.1 Code in mehreren Klassen gemeinsam nutzen 9.3.5 Die Verwendung von Vererbung 9.4 Zusammenfassung Folien mit gelben Punkten am oberen rechten Rand sind weniger wichtiger für das Verständnis der nachfolgenden Kapitel. Folien seit erster Vorstellung in Vorlesung (10.5.07) angepasst Eine Schnittstelle (abstrakter Datentyp) kann mehrere, verschiedene konkrete Datenstrukturen abstrahieren. Wird die Entscheidung, welche Funktion konkret die Schnittstellenfunktion implementiert, zur Compilezeit festgelegt, spricht man von statischer Bindung. Erfolgt diese Entscheidung zur Laufzeit, ist eine parallele Nutzung verschiedener Implementierungen möglich und man spricht von dynamischer Bindung. Dabei obliegt die Verantwortung für die Auswahl der richtigen Funktion dem Ersteller des Datentyps und nicht dem Nutzer. In diesem Fall kann eine Variable (von einen bestimmten (abstrakten) Datentyp) in verschiedener Gestalt vorkommen. Man spricht von Vielgestaltigkeit bzw. Polymorphie. V 4.01; Hon. Prof. Helmke 27 V 4.01; Hon. Prof. Helmke 28 Dynamisches Binden (2) Dynamisches Binden (3) In Anwendungen kann es durchaus vorkommen, dass in verschiedenen Kontexten andere Implementierungen desselben Datentyps oder Algorithmus' vorteilhaft sind. So könnte es bei unserer Netzplanung einen Transportvorgang geben, der durch die Anzahl der Teile, die einfache Transportdauer und die maximale Anzahl von Teilen pro Transport charakterisiert wird. Abhängig von diesen Größen ergibt sich die Dauer des Vorgangs. An anderer Stelle haben wir einen Produktionsvorgang, bei dem ein bestimmtes Produkt in einer bestimmten Menge gefertigt wird. Hier ergibt sich die Vorgangsdauer aus Umrüstzeiten und dem Produkt aus Anzahl und Produktionsdauer für ein Teil. Beides sind Vorgänge im Sinne unseres abstrakten Datentyps Vorgang. Dennoch benötigen wir unterschiedliche Implementierungen für Transportund Produktionsvorgänge, weil andere Attribute gespeichert werden müssen und sich die Vorgangsdauer unterschiedlich errechnet. V 4.01; Hon. Prof. Helmke 31 V 4.01; Hon. Prof. Helmke 32

Automatische Polymorphie (2) Automatische Polymorphie (3) Der Vorgang mit den Funktionszeigern und den Umleitungs- Schnittstellenfunktionen (siehe Buch, Kap. 9) wird durch eine Klasse oder in Java (wahlweise) durch ein Interface ersetzt, die nichts weiter als die Funktionsdeklarationen der Schnittstellenfunktionen enthält, aber keine Implementationen. Die administrativen Datenstrukturen (Funktionszeiger) und Umleitungsfunktionen werden praktisch vom Compiler erzeugt. Die konkreten Vorgänge (abgeleitete Klassen von Vorgang) werden anschließend in eigenen Klassen implementiert. Gibt es in Transportvorgang dann Funktionen mit identischen Rückgabewerten, Namen und Argumenten wie die Schnittstellenfunktion, so ordnet der Compiler diese Funktionen den Schnittstellenfunktionen zu (in unserem Modell entspricht das der Übernahme der Funktionszeiger). class Vorgang { virtual double getdauer() = 0; ; C++ interface Vorgang { double getdauer(); Java class Transportvorgang : public Vorgang { /* */ virtual double getdauer(); ; C++ class Transportvorgang implements Vorgang { /*...*/ double getdauer() { return...; Java V 4.01; Hon. Prof. Helmke 33 V 4.01; Hon. Prof. Helmke 34 Ist-Ein Beziehung Automatische Polymorphie (4) Vorgang Vorgang TransportVorgang ProduktionsVorgang TransportVorgang ProduktionsVorgang Grafische Notation für ist-ein -Beziehungen in UML. Je nachdem, ob es sich bei Vorgang um ein Interface handelt (rechts) oder um eine Klasse (links,) sind die Linien gestrichelt oder durchgezogen. Das vorherige Beispiel lässt sich somit in objektorientierten Sprachen viel einfacher umsetzen. Dabei ist für öffentliche Methoden der Schnittstelle, die dynamisch gebunden werden sollen, in C++ das Schlüsselwort virtual voranzustellen -- in Java werden immer alle Methoden dynamisch gebunden. Eine Klasse heißt abstrakte Klasse, wenn die Implementierung einzelner oder aller virtuellen Methoden fehlt, was in C++ auch direkt durch den Zusatz = 0 hinter der Funktionsdeklaration offenbar wird. Eine abstrakte Klasse ist unvollständig, weil die abstrakten Methoden in ihr nicht aufgerufen werden können, es gibt für sie noch keine zugeordnete Implementierung (Funktionzeiger ist noch NULL). Daher wird die Instanziierung (d.h. der Aufruf des Konstruktors) einer abstrakten Klasse auch vom Compiler mit einer Fehlermeldung quittiert. V 4.01; Hon. Prof. Helmke 35 V 4.01; Hon. Prof. Helmke 36

Anwendung Vorgang** vv = new Vorgang*[2]; vv[0] = new TransportVorgang(...); vv[1] = new ProduktionsVorgang(...); double gesamtdauer = 0; for (int i=0;i<2;++i) { gesamtdauer += vv[i]->getdauer(); C++ Vorgang vv[] = new Vorgang[2]; vv[0] = new TransportVorgang(...); vv[1] = new ProduktionsVorgang(...); double gesamtdauer = 0; for (int i=0;i<2;++i) { gesamtdauer += vv[i].getdauer(); Java Schon mal Clicker-Teile holen und aktivieren Nachdem wir neben einem Transportvorgang auch einen Produktionsvorgang in der beschriebenen Weise angelegt haben, können wir in einem Array von Vorgängen in beliebiger Reihenfolge mal Transport- und mal Produktionsvorgänge einfügen, wie obiges Beispiel zeigt: V 4.01; Hon. Prof. Helmke 37 V 4.01; Hon. Prof. Helmke 38 Statischer Typ und Dynamischer Typ Vorgang** vv = new Vorgang*[2]; vv[0] = new TransportVorgang(...); vv[0] = new ProduktionsVorgang(...); vv[1] = new TransportVorgang(...); double gesamtdauer = 0; for (int i=0;i<2;++i) { gesamtdauer += vv[i]->getdauer(); C++ Vorgang vv[] = new Vorgang[2]; vv[0] = new TransportVorgang(...); vv[0] = new ProduktionsVorgang(...); vv[1] = new TransportVorgang(...); double gesamtdauer = 0; for (int i=0;i<2;++i) { gesamtdauer += vv[i].getdauer(); Java Nochmalige Erklärung an einem einfachen Beispiel Jetzt, aber stark aus C++ Sicht Der statische Typ einer Variablen wird bei deren Deklaration festgelegt. Der statische Typ von vv[0] ist (für alle Zeiten) Vorgang oder ganz korrekt in C++ Zeiger auf Vorgang. Der dynamische Typ einer Variablen kann sich ändern, es ist der Typ des referenzierten Objekts. Zunächst ist der dynamische Typ von vv[0] TransportVorgang. Er ändert sich dann in ProduktionsVorgang. V 4.01; Hon. Prof. Helmke 39

Virtuelle Methoden Virtuelle Methoden (2) class Mitarbeiter { float Gehalt() const { return 2500; int Unterg() const { return 0; ; class Chef: public Mitarbeiter { float Gehalt() const { return 40000; int Unterg() const { return 20; ; void PrintPerson (const Mitarbeiter* pers) { cout<<pers->gehalt()<<" "<<pers->unterg(); Welche Methode wird jeweils aufgerufen? Chef::GetGehalt oder Mitarbeiter::GetGehalt? Ausgabe ist? 1. 2500 0 2500 0 2. 40000 20 40000 20 3. 21250 10 21250 10 Die Ausgabe ist: 4. 2500 25000 0 40000 20 2500 0 Das ist offensichtlich nicht das Gewünschte! Chef* schroeder = new Chef; Mitarbeiter* mueller = new Mitarbeiter; PrintPerson(mueller); cout << endl; PrintPerson(schroeder); In beiden Fällen werden die Methoden der Basisklasse aufgerufen, da bereits zur Übersetzungszeit auf Grund des Zeigertyps Mitarbeiter* pers die Methode gemäß dem statischen Typ Mitarbeiter* ausgewählt wird (statische Bindung). Wird jedoch in der Basisklasse der Deklaration der Methoden Gehalt und Unterg das Schlüsselwort virtual vorangestellt, so wird erst zur Laufzeit aufgrund der Klasse des aktuellen Objektes entschieden, welche Methode wirklich abgearbeitet wird (dynamische Bindung), beim zweiten Aufruf also Chef::Gehalt und Chef::Unterg. V 4.01; Hon. Prof. Helmke 41 V 4.01; Hon. Prof. Helmke 42 Virtuelle Methoden (3) Virtuelle Methoden class Mitarbeiter { virtual float Gehalt() const { return 2500; virtual int Unterg() const { return 0; ; class Chef: public Mitarbeiter { virtual float Gehalt() const { return 40000; virtual int Unterg() const { return 20; ; void PrintPerson (const Mitarbeiter* pers) { cout<<pers->gehalt()<<" "<<pers->unterg(); Jetzt werden die Methoden gemäß dem dynamischen Typ -- d.h. gemäß dem Typ des Objektes, an das der Zeiger gebunden ist -- selektiert! Die Ausgabe ist also: 2500 0 40000 20 Mitarbeiter* schroeder = new Chef; Mitarbeiter* mueller = new Mitarbeiter; PrintPerson(mueller); cout << endl; PrintPerson(schroeder); class Mitarbeiter { virtual float Gehalt() const { return 2500; virtual int Unterg() const { return 0; ; class Chef: public Mitarbeiter { virtual float Gehalt() const { return 40000; virtual int Unterg() const { return 20; ; Welche Methode wird jeweils aufgerufen? Chef::GetGehalt oder Mitarbeiter::GetGehalt? Ausgabe ist? 1. 2500 0 2500 0 2. 2500 0 40000 20 3. 21250 10 21250 10 4. 40000 0 40000 10 void PrintPerson (const Mitarbeiter& pers) { cout<<pers.gehalt()<<" "<<pers.unterg(); Chef* schroeder = new Chef; Mitarbeiter* mueller = new Mitarbeiter; PrintPerson(*mueller); cout << endl; PrintPerson(*schroeder); V 4.01; Hon. Prof. Helmke 43 V 4.01; Hon. Prof. Helmke 44

Virtuelle Methoden Virtuelle Methoden (4) class Mitarbeiter { virtual float Gehalt() const { return 2500; virtual int Unterg() const { return 0; ; class Chef: public Mitarbeiter { virtual float Gehalt() const { return 40000; virtual int Unterg() const { return 20; ; Welche Methode wird jeweils aufgerufen? Chef::GetGehalt oder Mitarbeiter::GetGehalt? Ausgabe ist? 1. 2500 0 2500 0 2. 2500 0 40000 20 3. 21250 10 21250 10 4. 40000 0 40000 10 void PrintPerson (const Mitarbeiter& pers) { cout<<pers.gehalt()<<" "<<pers.unterg(); Chef* schroeder = new Chef; Mitarbeiter* mueller = new Mitarbeiter; PrintPerson(*mueller); cout << endl; PrintPerson(*schroeder); class Mitarbeiter {... wie bei (3)... ; class Chef: public Mitarbeiter {... wie bei (3)... ; void PrintPerson1 (const Mitarbeiter* pers) { cout<<pers->gehalt()<<" "<<pers->unterg(); void PrintPerson2 (const Mitarbeiter& pers) { cout<<pers.gehalt()<<" "<<pers.unterg(); void PrintPerson3 (Mitarbeiter pers) { cout<<pers.gehalt()<<" "<<pers.unterg(); Dynamisches Binden funktioniert sowohl bei Bindung über Zeiger als auch bei Bindung über Referenzen, d.h. hier bei den Methoden PrintPerson1 und PrintPerson2. Dynamisches Binden funktioniert nicht beim direkten Zugriff, d.h. hier bei der Methode PrintPerson3. V 4.01; Hon. Prof. Helmke 45 V 4.01; Hon. Prof. Helmke 46 Virtuelle Methoden (5) Die Klasse Mitarbeiter ist polymorph, weil Zeiger vom Typ Mitarbeiter* sowohl an Objekte vom Basistyp Mitarbeiter als auch an Objekte vom abgeleiteten Typ Chef gebunden werden können. Wird die Methode aus der Basisklasse oder aus abgeleiteten Klasse aufgerufen? Wir unterscheiden bei jeder Variablen zwischen dem dynamischen Typ und dem statischen Typ. Eine Klasse ist polymorph, wenn sie mindestens eine Methode besitzt (geerbt oder selbst deklariert), die virtuell ist, sodass diese dann in einer abgeleiteten Klasse redefiniert (überschrieben) werden kann. Beispiel Obst* ob1 = new Apfel(); Der statische Typ von ob1 ist immer Obst (Zeiger auf Obst). Der dynamische Typ von ob1 kann sich ändern und ist zur Zeit Apfel (Zeiger auf Apfel ganz genau). V 4.01; Hon. Prof. Helmke 47 V 4.01; Hon. Prof. Helmke 48

Wird die Methode aus der Basisklasse oder aus abgeleiteten Klasse aufgerufen? Liegt ein Wert (also keine Referenz und kein Zeiger) vor, ist der statische Typ immer gleich dem dynamischen Typ. (Merksatz M1) Nur bei Referenz oder Zeigern können der dynamische und statische Typ sich unterscheiden (d.h. der Typ der Instanz, auf die verwiesen wird). (Merksatz M2) Bei virtuellen Methoden entscheidet der dynamische Typ über die Methode, die aufgerufen wird. (Merksatz M3) Bei nicht virtuellen Methoden entscheidet der statische Typ über die Methode, die aufgerufen wird. (Merksatz M4) Clicker void funk() { Vorgang* vv[3] ; vv[0] = new TransVorg (); vv[1] = new ProdVorg (); vv[2] = new TransVorg(); for (int i=0;i<3;++i) { vv[i]->print(); class Vorgang { virtual void print() const {cout << "V"; class TransVorg : public Vorgang { virtual void print() const {cout << "T"; class ProdVorg : public Vorgang { virtual void print() const {cout << "P"; Wie ist die Ausgabe auf dem Bildschirm nach Ausführung von funk? 1. VVV 2. TPT 3. VTP 4. TTT Bei virtuellen Methoden entscheidet der dynamische Typ über die Methode, die aufgerufen wird. (Merksatz M3) Ergebnis: 1 2 3 4 (wg. M2 und M3) V 4.01; Hon. Prof. Helmke 49 V 4.01; Hon. Prof. Helmke 50 Clicker Clicker void funk() { Vorgang* vv[3]; vv[0] = new TransVorg (); vv[1] = new ProdVorg (); vv[2] = new TransVorg (); for (int i=0;i<3;++i) { vv[i]->print(); class Vorgang { void print() const {cout << "V"; class TransVorg : public Vorgang { void print() const {cout << "T"; class ProdVorg : public Vorgang { void print()const {cout << "P"; Wie ist die Ausgabe auf dem Bildschirm nach Ausführung von funk? 1. VVV 2. TPT 3. VTP 4. TTT Bei nicht virtuellen Methoden entscheidet der statische Typ über die Methode, die aufgerufen wird. (Merksatz M4) void funk() { Vorgang v; TransVorg tv; ProdVorg pv; Vorgang v[3]; v[0] = v; v[1]=tv; v[2]=pv; for (int i=0; i<3; ++i) { v[i].print(); class Vorgang { virtual void print() const {cout << "V"; class TransVorg : public Vorgang { virtual void print() const {cout << "T"; class ProdVorg : public Vorgang { virtual void print() const {cout << "P"; Wie ist die Ausgabe auf dem Bildschirm nach Ausführung von funk? 1. VVV 2. TPT 3. VTP 4. TTT Liegt ein Wert (also keine Referenz und kein Zeiger) vor, ist der statische Typ immer gleich dem dynamischen Typ. (Merksatz M1) Ergebnis: 1 2 3 4 (wg. M2 und M4) V 4.01; Hon. Prof. Helmke 51 Ergebnis: 1 2 3 4 V 4.01; Hon. Prof. Helmke 52

Clicker void funk() { Vorgang** vv = new Vorgang*[3]; vv[0] = new TransVorg (); vv[1] = new ProdVorg (); vv[2] = new TransVorg(); for (int i=0;i<3;++i) { vv[i]->print(); class Vorgang { virtual void print() const {cout << "V"; class TransVorg : public Vorgang { virtual void print() const {cout << "T"; class ProdVorg : public Vorgang { virtual void print() const {cout << "P"; Wie ist die Ausgabe auf dem Bildschirm nach Ausführung von funk? 1. VVV 2. TPT 3. VTP 4. TTT Ergebnis: 1 2 3 4 V 4.01; Hon. Prof. Helmke 53 Virtuelle Destruktoren class A { int* px; A() {cout<<"a+"; px = new int; virtual ~A() {cout<<"a-"; delete px; ; class B : public A { int* py; B() {cout<<"b+"; py = new int; virtual ~B() {cout<<"b-"; delete py; ; void f() { A* pa = new B; delete pa; int main() { f(); // Speicherleck, wenn Destruktor // in Basisklasse nicht virtuell! Ausgabe mit virt. Destr.: A+B+B-A- Ausgabe ohne virt. Destr.: A+B+A- V 4.01; Hon. Prof. Helmke 55 Virtuelle Destruktoren class A { int* px; A(int w) {cout<<"a+"; px = new int(w); virtual ~A() {cout<<"a-"; delete px; ; class B : public A { int* py; B(int aw, int bw) : A(aw) { cout<<"b+"; py = new int(bw); virtual ~B() {cout<<"b-"; delete py; ; void f() { A a(17); B b(16, 33); int main() { f(); // Speicherleck, wenn Destruktor // in Basisklasse nicht virtuell! Werte- und Referenzsemantik in Zusammenhang mit Polymorphie Immer wenn Variablen in Wertesemantik vorliegen, ist ihr Typ genau bekannt und Funktionsaufrufe können statisch gebunden werden. Wenn verschiedene Realisierungen des Datentyps unterschiedlich groß sein können und wir im Sinne eines polymorphen Datentyps nicht wissen, welche Gestalt ein Vorgang gerade annimmt, ist das Anlegen einer polymorphen Variable in Wertesemantik schlicht unmöglich. Eine Vorbedingung für das Auftreten von dynamischer Bindung ist also, dass wir es mit Zeigern oder Referenzen zu tun haben. Da in C++ der Anwender entscheiden soll, ob er gerade Polymorphie nutzen will oder nicht, gesteht C++ dem Entwickler das Recht zu, konkrete Objekte sowohl in Werte- als auch in (polymorphiefähiger) Zeiger/Referenz-Semantik zu verwenden. In Java werden alle Funktionsaufrufe dynamisch gebunden, entsprechend sind alle Variablen in Java -- bis auf die elementaren Datentypen -- Referenzen auf Objekte (im Heap). V 4.01; Hon. Prof. Helmke 56 V 4.01; Hon. Prof. Helmke 57

Werte- und Referenzsemantik in Zusammenhang mit Polymorphie (2) Schreibt der C++-Anwender vector<transportvorgang>, so drückt er damit aus, dass er Wert-Instanzen von (ausschließlich) Transportvorgängen speichern möchte (bestehend aus Anzahl, Weglänge und Einheitsdauer). Der Typ ist damit eindeutig festgelegt und Polymorphie ist unnötig (und unmöglich). Zusammenfassung Die dynamische Bindung kann nur dort erfolgen, wo über den Punktoperator (oder Pfeiloperator) eine Methode aufgerufen wird, die mit identischem Prototyp mehrfach in verschiedenen Implementierungen realisiert wurde (Überschreiben des Funktionszeigers). Schreibt er hingegen vector<transportvorgang*> so drückt er damit aus, dass er Referenzen auf Transportvorgänge speichern will, wobei es dann durchaus möglich ist, dass es mehrere, verschiedene (Unterklassen von) Transportvorgänge geben kann, von denen automatisch die richtige Funktion aufgerufen wird. Diese Überlegungen braucht ein Java-Entwickler nicht anzustellen; semantisch entspricht bei ihm Vector<Vorgang> immer der C++-Variante vector<vorgang&> (oder vector<vorgang*>). V 4.01; Hon. Prof. Helmke 58 V 4.01; Hon. Prof. Helmke 62 Zusammenfassung (2) class Vorgang { int getid() {.; virtual double getdauer() = 0; virtual double getfruehanf() {..; ; Und genau aus diesen Gründen (und sonst keinen) sollte eine Methode als virtuell (virtual), rein virtuell oder nicht virtuell deklariert werden. class Vorgang { // kann nicht überschrieben werden int getid() {.; // muss überschrieben werden virtual double getdauer() = 0; // kann überschrieben werden virtual double getfruehanf() {..; /*Abstrakte Methode können sogar implementiert werden und bieten Erben Default-Implementieung zur Nutzung an */ virtual int meth() = 0 {. ; ; V 4.01; Hon. Prof. Helmke 63