Die Präsentation wird geladen. Bitte warten

Die Präsentation wird geladen. Bitte warten

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

Ähnliche Präsentationen


Präsentation zum Thema: "Polymorphie (Vielgestaltigkeit). Wenn eine Methode, wie z.B. print für verschiedene Programmteile steht (und z.B. einmal Objekte verschiedener Klassen."—  Präsentation transkript:

1 Polymorphie (Vielgestaltigkeit)

2 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

3 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

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

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

6 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.

7 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.

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

9 In UML-Darstellung:

10 Form Rechteck Kreis

11 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.

12 Als Programm-Skizze

13 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.

14 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

15 Das exakte Programm:

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

17 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.

18 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.

19 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).

20 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).

21 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.

22 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.

23 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.

24 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.

25 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.

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

27 Motivation

28 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.

29 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).

30 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.

31 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.

32 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).

33 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;

34 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) ?

35 Hier nochmals das Programm:

36 #include "stdafx.h" #include

37 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

38 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).

39 // 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.

40 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).

41 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.

42 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).


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

Ähnliche Präsentationen


Google-Anzeigen