Polymorphie (Vielgestaltigkeit). Wenn eine Methode, wie z.B. print für verschiedene Programmteile steht (und z.B. einmal Objekte verschiedener Klassen.

Slides:



Advertisements
Ähnliche Präsentationen
der Universität Oldenburg
Advertisements

der Universität Oldenburg
1 Funktionstemplates Einbinden von Templates: –Eine *.o Datei, die durch Übersetzen einer Datei nur mit Templates erzeugt wurde, enthält keinen Programmcode.
Abstrakte Klassen Basisklassen sollten in der Regel sehr allgemein sein. Oft ist es nicht notwendig, dass Objekte dieser generellen Basisklassen angelegt.
Objektorientierte Programmierung
Konzepte objektorientierter Systeme
Definition von Klassen in Java
Java: Objektorientierte Programmierung
Indirekte Adressierung
Java: Grundlagen der Objektorientierung
Abstrakte Klassen.
Klassenvariable (auch Klassendaten bzw. statische Attribute genannt) und statische Methoden.
Dateien. Eine Datei wird in C++ als ein Stream, also als ein Objekt einer bestimmten Klasse dargestellt.
Erweiterte Zuweisungskompatibilität
Klassenvariable. Da man für jede Kuh bzw. jede Henne auf dem Markt den gleichen Preis für ein Liter Milch, bzw. den gleichen Preis für ein Ei bekommt,
Konstruktoren.
Vererbung. Das Prinzip der Vererbung im täglichen Leben:
Polymorphie (Vielgestaltigkeit)
Polymorphie (Vielgestaltigkeit)
Objekte und Arbeitsspeicher
Interface bzw. Schnittstelle anschaulich: Hüllenklasse
Dynamischer Speicher. In einer Funktion wird z.B. mit der Deklaration int i; Speicher auf dem sogenannten Stack reserviert. Wenn die Funktion verlassen.
Einführung in die Programmierung mit Java
Programmieren mit JAVA
Abstrakter Datentyp in C++ I - Klasse -
C++ Vererbung und Polymorphie
DVG Einführung in Java1 Einführung in JAVA.
DVG Klassen und Objekte
FH-Hof Java2D - Grundlagen Richard Göbel. FH-Hof Java2D - Funktionen Java2D unterstützt: das Zeichnen von Grafiken die Darstellung von Texten die Darstellung.
Seite 1 Interface - Konzept Ein Interface führt einen neuen Datentyp ein: interface Frau {... } Das Interface enthält Deklarationen ( keine Definitionen.
Prof. Dr. Gerhard Schmidt pres. by H.-J. Steffens Software Engineering SS 2009Folie 1 Objektmodellierung Objekte und Klassen Ein Objekt ist ein Exemplar.
Einführung in die Programmierung Wintersemester 2013/14 Prof. Dr. Günter Rudolph Lehrstuhl für Algorithm Engineering Fakultät für Informatik TU Dortmund.
Entwurfs- und Implementationsdiagramme
Einführung in die Programmierung Wintersemester 2008/09 Prof. Dr. Günter Rudolph Lehrstuhl für Algorithm Engineering Fakultät für Informatik TU Dortmund.
Einführung in die Informatik für Naturwissenschaftler und Ingenieure
Einführung in die Programmierung Wintersemester 2008/09 Prof. Dr. Günter Rudolph Lehrstuhl für Algorithm Engineering Fakultät für Informatik TU Dortmund.
Einführung in die Informatik für Naturwissenschaftler und Ingenieure (alias Einführung in die Programmierung) (Vorlesung) Prof. Dr. Günter Rudolph Fachbereich.
Einführung in die Programmierung Wintersemester 2010/11 Prof. Dr. Günter Rudolph Lehrstuhl für Algorithm Engineering Fakultät für Informatik TU Dortmund.
OOP-Begriffe Abstraktion Modellieren Klasse Objekt Attribute Methoden
Objektorientiertes Konstruieren
Programmiervorkurs WS 2014/15 Instanzmethoden
early binding (frühe Bindung) late binding (späte Bindung)
Informatik I : Software höhere Programmiersprachen Java Klassen: hat Methoden (Funktionen) und Daten (Variablen) es kann mehrere Klassen geben nur eine.
Java-Kurs Übung Besprechung der Hausaufgabe Vererbung
Java-Kurs Übung Besprechung der Hausaufgabe Vererbung
Einführung in die Programmierung mit Java
Institut für Kartographie und Geoinformation Prof. Dr. Lutz Plümer, Dr. Thomas H. Kolbe Einführung in die Programmierung mit Java 9. Vorlesung WS 2001/2002.
Sichtbarkeit einschränken
Erweiterte Zuweisungskompatibilität. Wie kann man Objekte verschiedener Klassen einer Klassenhierarchie einander zuweisen ?
Objektorientierte Programmierung (OOP)
Pointer. Grundsätzliches: Im Arbeitsspeicher werden Daten gespeichert. Um auf die Daten eindeutig zugreifen zu können, werden diesen Daten Adressen zugeordnet.
Java Programme nur ein bisschen objektorientiert.
Tutorium Software-Engineering SS14 Florian Manghofer.
Tutorium Software-Engineering SS14 Florian Manghofer.
Tutorium Software-Engineering SS14 Florian Manghofer.
Tutorium Software-Engineering SS14 Florian Manghofer.
Vererbung in Java. public abstract class Form { protected int breite; protected int hoehe; protected String farbe; /** * Erzeuge eine Form der Breite.
Konstruktoren.
Objektorientierung Gliederung von Daten und Funktionen zu Objekten
Venusspiegel und Marsschild
OOP II.
Java-Kurs Übung Klassen und Objekte: Vererbung (Fortsetzung)
Programmierung und Vererbung in Java
Es gibt Klassen, die mit der Entwicklungsumgebung ausgeliefert werden
Definition Felder Konstruktor Methoden Beispiel
9. Vererbung und Polymorphie
Implementieren von Klassen
Vererbung in Java Von Jan und Eddi.
Polymorphie Überschreiben
 Präsentation transkript:

Polymorphie (Vielgestaltigkeit)

Wenn eine Methode, wie z.B. print für verschiedene Programmteile steht (und z.B. einmal Objekte verschiedener Klassen wie z.B. Rechtecke oder das andere Mal Kreise auf dem Bildschirm ausgeben bzw. zeichnen kann), dann nennt man dies Polymorphie

Beispiel: In einem Feld sollen verschiedene Objekte (entweder Rechtecke oder Kreise) abgespeichert und dann (z.B. zu Testzwecken) deren spezifischen Daten (Länge und Breite bzw. Radius) ausgegeben werden

Die Reihenfolge der Objekte (Rechteck bzw. Kreis) in dem Feld soll vom Anwender bestimmt werden bzw. zufällig sein.

Aber Halt: In einem Feld können nur Objekte der gleichen Klasse gespeichert werden.

Deswegen muß man eine Oberklasse von Rechteck und Kreis erzeugen und Objekte (genauer Pointer; siehe später) der Oberklasse Objekte der Unterklassen Rechteck oder Kreis zuweisen und die Objekte der Oberklasse im Feld speichern.

Vorgehensweise: Man erzeugt die Klassen Rechteck und Kreis mit jeweils der spezifischen print Funktion, die den Flächeninhalt und den Umfang des Rechtecks bzw. des Kreises ausgibt.

Dann erzeugt man eine gemeinsame Basisklasse, z.B. mit dem Namen Form der Klassen Rechteck und Kreis.

In UML-Darstellung:

Form Rechteck Kreis

Mit new werden (in einer Schleife) jeweils Pointer auf ein Rechteck bzw. einen Kreis erzeugt und einem Pointer auf Form zugewiesen und dieser Pointer in einem Element des Feldes abgespeichert.

Als Programm-Skizze

Bemerkung: Der Einfachheit halber ist die Reihenfolge der Rechtecke bzw. Kreise in der folgenden Programm-Skizze nicht zufällig bzw. vom Anwender vorgegeben und außerdem ist die Schleife weggelassen worden.

Form *f[10]; Kreis *kp; Rechteck *rp; kp = new Kreis(); rp = new Rechteck(); f[0] = rp; f[1] = kp; f[0]->print(); f[1]->print(); Einem Pointer auf Form wird ein Pointer auf Rechteck zugewiesen Einem Pointer auf Form wird ein Pointer auf Kreis zugewiesen Die print() Methode wird aufgerufen, die in der Klasse Rechteck implementiert wurde. Die print() Methode wird aufgerufen, die in der Klasse Kreis implementiert wurde. Dazu muß print als virtuelle Funktion deklariert werden, d.h. der Name print wird an die Funktion nicht während der Kompilierung gebunden, sondern während der Laufzeit

Das exakte Programm:

#include "stdafx.h" #include using namespace std;

class Form{ public: virtual void print(); }; // Form class Rechteck: public Form{ private: double l; double b; public: Rechteck(double ll, double bb); ~Rechteck(); virtual void print(); }; // Rechteck Durch den Bezeichner virtual wird print eine virtuelle Funktion. Die Verwendung von virtual ist nur innerhalb einer Klassendeklaration zulässig. Der Bezeichner virtual vererbt sich und muß daher nicht mehr (kann aber) in den Subklassen wiederholt zu werden.

class Kreis: public Form{ private: double r; public: Kreis(double rr); ~Kreis(); virtual void print(); }; // Kreis Der Bezeichner virtual vererbt sich und muß daher nicht mehr (kann aber) in den Subklassen wiederholt zu werden.

void Form::print(){ }; Rechteck::Rechteck(double ll, double bb){ l = ll; b = bb; }; Rechteck::~Rechteck(){ }; void Rechteck::print(){ cout << "Rechteck: Laenge =" << l << " Breite =" << b; }; print wird in der Klasse Rechteck redefiniert (neu implementiert).

Kreis::Kreis(double rr){ r = rr; } Kreis::~Kreis(){ } void Kreis::print(){ cout << "Kreis: Radius = " << r << endl; } print wird in der Klasse Kreis redefiniert (neu implementiert).

int main(){ int r,i; Form *v[10]; srand((unsigned)time(NULL)); for(i=0;i<10;i++){ r = rand()%2; if(r==0) v[i] = new Kreis(100*i); else v[i] = new Rechteck(10*i, 20*i); } Es wird ein Feld v mit 10 Pointern auf Objekte der Klasse Form reserviert. r ist eine Zufallszahl, die entweder den Wert 0 oder 1 annimmt. legt ein Objekt der Klasse Kreis an, liefert einen Pointer darauf zurück und weist ihn einem Feldelement (pointer auf Form) zu. Dies funktioniert, weil: "Oberklasse = Unterklasse" korrekt ist. legt ein Objekt der Klasse Kreis an, liefert einen Pointer darauf zurück.

for(i=0;i<10;i++){ v[i]->print(); } for(i=0;i<10;i++){ delete v[i]; } return(0); } delete gibt den dynamisch erzeugten Speicher wieder frei. Gibt jeweils den Radius des Kreises eines Feldelements bzw. Breite und Länge des Rechtecks aus.

Frage: Wäre das folgende abgeänderte Programm auch korrekt ? Bem: Im Vergleich zum letzten Programm wurde nur der rot gekennzeichnete Quellcode in main() geändert.Alles andere ist gleich.

void main(){ int r; int i; Form vv[10]; srand((unsigned)time(NULL)); Es wird ein Feld vv mit 10 Objekten der Klasse Form reserviert.

for(i=0;i<10;i++){ Rechteck re(10*i, 20*i); Kreis kr(100*i); r = rand()%2; if(r==0) vv[i] = kr; else vv[i] = re; } for(i=0;i<10;i++) vv[i].print(); } legt bei jedem Durchgang ein Objekt der Klasse Kreis an. Späte Bindung funktioniert nur, wenn der Zugriff über einen Pointer auf ein Objekt der Basisklasse erfolgt. Da dies hier nicht der Fall ist, wird die frühe Bindung verwendet. Der Typ der Variablen vv (also Form) bestimmt, welche print()-Funktion aufgerufen wird. Diese print()- Funktion der Klasse Form gibt aber nichts auf dem Bildschirm aus. legt bei jedem Durchgang ein Objekt der Klasse Rechteck an.

Rein virtuelle Funktionen auch pure function (englisch) oder abstrakte Funktionen genannt

Motivation

Hat es Sinn, im letzten Programm eine Instanz der Klasse Form zu bilden ? Nein, die Klasse Form ist nur dazu da, eine Basisklasse der Klassen Rechteck und Kreis zu bilden. Die Klasse Form besitzt nicht einmal ein Attribut, sondern nur die Methode print(), die nichts macht.

Was passiert, wenn der Programmierer aus Versehen vergessen hat, in einer der Subklassen, z.B. Rechteck, die print() Methode zu deklarieren und zu definieren (implementieren) ? Dann wird die virtuelle Funktion print() der Basisklasse in die Subklasse Rechteck vererbt. Ein Aufruf der Funktion print() der Klasse Rechteck macht dann also das gleiche wie ein Aufruf der Funktion print() der Klasse Form (nämlich gar nichts, weil sie keine Anweisungen besitzt).

Um zu verhindern, daß 1) Instanzen einer Klasse gebildet werden und um zu verhindern, daß 2) ein Programmierer vergisst, eine virtuelle Funktion in einer Subklasse zu deklarieren und zu definieren (implementieren), wurden die sogenannten rein virtuellen Funktionen eingeführt.

Eine rein virtuelle Funktion (abstrakte Funktion) ist eine virtuelle Funktion mit dem Zusatz =0 Beispiel: virtual print()=0; Das =0 bedeutet: Diese Funktion muss nicht (kann aber) in der Klasse implementiert werden.

Besitzt eine Klasse mindestens eine abstrakte Funktion (rein virtuelle Funktion), dann heißt diese Klasse abstrakte Klasse. Von dieser darf dann keine Instanz gebildet werden (wohl aber Pointer auf Objekte dieser abstrakten Klasse).

Was muss man im letzten Programm abändern, damit sichergestellt wird, daß von der Klasse Form keine Instanz gebildet werden kann ? Die Programmierzeile: virtual void print(); in der Klasse Form muß ersetzt werden durch: virtual void print()=0;

Was passiert, wenn der Programmierer im letzten Programm aus Versehen vergessen hat, in einer der Subklassen, z.B. Rechteck, die print() Methode zu deklarieren und zu definieren (implementieren) ?

Hier nochmals das Programm:

#include "stdafx.h" #include

class Form{ public: virtual void print()=0; }; // Form class Rechteck: public Form{ private: double l; double b; public: Rechteck(double ll, double bb); ~Rechteck(); // virtual void print(); }; // Rechteck Durch die Bezeichnung = 0 wird eine rein virtuelle Funktion deklariert. Es wurde "vergessen", print() zu deklarieren

class Kreis: public Form{ private: double r; public: Kreis(double rr); ~Kreis(); virtual void print(); }; // Kreis Alles wie im letzten Programm (nichts wurde verändert).

// void Form::print(){ // }; Rechteck::Rechteck(double ll, double bb){ l = ll; b = bb; }; Rechteck::~Rechteck(){ }; /* void Rechteck::print(){ cout << "Rechteck: Laenge =" << l << " Breite =" << b; }; */ Es wurde "vergessen", print() zu definieren (implementieren) print() braucht in Form nicht mehr implementiert zu werden.

Kreis::Kreis(double rr){ r = rr; } Kreis::~Kreis(){ } void Kreis::print(){ cout << "Kreis: Radius = " << r << endl; } Alles wie im letzten Programm (nichts wurde verändert).

void main(){ int r,i; Form *v[10]; srand((unsigned)time(NULL)); for(i=0;i<10;i++){ r = rand()%2; if(r==0) v[i] = new Kreis(100*i); else v[i] = new Rechteck(10*i, 20*i); } Aber was meint der Compiler dazu, daß ein Objekt der Klasse Rechteck angelegt wird ? Alles wie im letzten Programm (nichts wurde verändert). Da die print() Funktion in der Klasse Rechteck fehlt (und damit nicht redefiniert wird), erbt Rechteck von Form die rein virtuelle Funktion print(). Da dann Rechteck eine rein virtuelle Funktion besitzt, kann auch keine Instanz von Rechteck erzeugt werden --> Compiler meldet Fehler. Damit kann der vergessliche Programmierer gezwungen werden, die Funktion print() in den Subklassen zu implementieren.

for(i=0;i<10;i++){ v[i]->print(); } for(i=0;i<10;i++){ delete v[i]; } Alles wie im letzten Programm (nichts wurde verändert).