Unter- und Oberklassen: Beispiel type KFZ = class kennzeichen: String; ... end; PKW = class(KFZ) tueren: integer end; LKW = class(KFZ) nutzlast: integer end; ... var k: KFZ; p: PKW; ... p := PKW.Create; p.kennzeichen := "DD-XX 1"; p.tueren := 5; k := p; ... Zugriff auf k.kennzeichen ... • Jeder PKW ist ein KFZ: Die Zuweisung eines Objekts der Unterklasse an eine Variable der Oberklasse ist zulässig. • Bei der Zuweisung gehen keinerlei Informationen verloren, das Objekt bleibt unverändert. Technische Universität Dresden Prof. Hußmann Informatik II (Maschinenwesen) Typanpassung type KFZ = class kennzeichen: String; ... end; PKW = class(KFZ) tueren: integer end; LKW = class(KFZ) nutzlast: integer end; ... var k: KFZ; p: PKW; ... p := PKW.Create; p.kennzeichen := "DD-XX 1"; p.tueren := 5; k := p; ... ... Zugriff auf k.tueren ... ->Fehlermeldung ... Zugriff auf (k as PKW).tueren • Der "as" Operator dient zur Typanpassung innerhalb von Klassenhierarchien. – "k as PKW" ist vom Typ "PKW" – Die Typanpassung kann fehlschlagen (Laufzeitfehler). Technische Universität Dresden Prof. Hußmann Informatik II (Maschinenwesen) Regeln zur Typanpassung • Jedes Objekt hat immer eine eindeutige Klassenzugehörigkeit, die sich von der Erzeugung bis zur Löschung nicht ändert. • Einer Variable von einem Klassentyp K können Objekte der Klasse K und jeder Unterklasse von K zugewiesen werden. • Der Zugriff auf ein Datenfeld, eine Methode oder eine Eigenschaft eines Objekts o ist nur möglich, wenn dies in der Klassendefinition der Klasse von o vorgesehen ist. • Wenn ein Objekt o1 von einer Unterklasse K1 einer Klasse K ist, aber über eine Variable der Klasse K zugänglich ist, so kann o1 mittels des "as"-Operators (o1 as K1) auch wie ein Objekt der Klasse K1 behandelt werden ("Typanpassung", "down-cast"). • Seien K1 und K2 verschiedene Unterklassen von K, o1 ein Objekt der Klasse K1, das über eine Variable der Klasse K zugänglich ist. Die Typanpassung "o1 as K2" führt zu einem Laufzeitfehler. • Ein Test, ob ein Objekt o einer Klasse K zur Unterklasse K1 gehört, ist mit "o is K1" möglich. Technische Universität Dresden Prof. Hußmann Informatik II (Maschinenwesen) Frühe und späte Bindung • Frühe Bindung: – Im allgemeinen wird die bei Eintreffen einer bestimmten Nachricht aufzurufende Methode bereits zur Übersetzungszeit, abhängig von der Klassenzugehörigkeit des angesprochenen Objekts, bestimmt. • Späte Bindung: – In manchen Fällen ist zur Übersetzungszeit nur eine Oberklasse des zur Laufzeit angesprochenen Objekts bekannt. In diesem Fall kann man die Wahl der aufzurufenden Methode von der erst zur Laufzeit bekannten Unterklasse abhängig machen. – Späte Bindung in Object Pascal: "virtuelle Methoden", Schlüsselworte virtual und dynamic (Unterschied zwischen virtual/dynamic nur in Optimierungen.) – Deklaration von Methoden, die virtuelle Methoden (in Unterklassen) "überdefinieren": Schlüsselwort override – Implementierung: Virtual Method Table (VMT) Technische Universität Dresden Prof. Hußmann Informatik II (Maschinenwesen) Polymorphie von Methoden (1) type KFZ = class kennzeichen: String; ... procedure anmelden(k: KFZDaten); virtual; end; PKW = class(KFZ) tueren: integer ... procedure anmelden(k: KFZDaten); override; end; LKW = class(KFZ) nutzlast: integer ... procedure anmelden(k: KFZDaten); override; var k: KFZ; p: PKW; ... p := PKW.Create; ... p.kennzeichen := "DD-XX 1"; p.tueren := 5; k := p; k.anmelden(...); Technische Universität Dresden Prof. Hußmann Informatik II (Maschinenwesen) Polymorphie von Methoden (2) • Polymorphie: – gleicher Methodenname in Ober- und Unterklasse – semantische Ähnlichkeit (!) der Methodenrümpfe – dynamische Auswahl der Implementierung • Prinzip: TObject MethodenVererbung KFZ OperationenSuche zur Laufzeit nach der speziellsten Version ("dynamisches Binden") PKW Technische Universität Dresden Prof. Hußmann Informatik II (Maschinenwesen) TObject • Alle Klassen in Object Pascal sind implizit von der Basisklasse TObject abgeleitet. • Ungefähre Struktur: TObject = class constructor Create; // Speicherplatzreservierung // Festlegung der Adresse (Identität) procedure Free; //Aufruf von Destroy //falls Adresse ≠ nil destructor Destroy; virtual; end; Technische Universität Dresden Prof. Hußmann Informatik II (Maschinenwesen) Konstruktoren für Unterklassen • Möglichkeit 1: Standardkonstruktor (aus TObject) verwenden. var obj: K; obj := K.Create; • Möglichkeit 2: Eigener Konstruktor. type K = class(K0) var X, Y: integer; constructor Create(Xinit,Yinit: integer); end; constructor K.Create(Xinit,Yinit : integer); begin inherited Create; X := Xinit; Y := Yinit; end; var obj: K; obj := K.Create(10,20); • Möglichkeit 3: Standardkonstruktor (aus TObject) verwenden und eigene Initialisierungsprozedur ("Init"). Technische Universität Dresden Prof. Hußmann Informatik II (Maschinenwesen) Destruktoren für Unterklassen type K = class(K0) var X, Y: integer; constructor Create(Xinit,Yinit: integer); ... destructor Destroy; override; end; • Regeln für Destruktoren: – Immer indirekt über Free aufrufen (vermeidet Laufzeitfehler) – Im Rumpf immer als letzte Aktion Destruktor der Oberklasse aufrufen. destructor K.Destroy(Xinit,Yinit : integer); begin ... inherited Destroy; end; Technische Universität Dresden Prof. Hußmann Informatik II (Maschinenwesen) Beispiel: Bauteileverwaltung • Jedes Bauteil hat eine Bauteilnummer (Zeichenreihe) und einen Preis (Ganzzahl). • Bauteile können Elemente und Baugruppen sein. Eine Baugruppe besteht aus mehreren Bauteilen. • Für ein Element ist der Preis direkt festgelegt, für Baugruppen berechnet er sich aus der Summe der Preise der enthaltenen Bauteile. Technische Universität Dresden Prof. Hußmann Informatik II (Maschinenwesen) Klassenstruktur für Bauteile-Beispiel (1) type TBauteil = class bauteilNr: string; function gibPreis: integer; virtual; end; TElement = class(TBauteil) preis: integer; constructor Create(n: string; p: integer); function gibPreis: integer; override; end; TBaugruppe = class(TBauteil) bauteile: array of TBauteil; constructor Create(n: string); procedure bauteilHinzu (b: TBauteil); function gibPreis: integer; override; function gibAnzahl: integer; end; Technische Universität Dresden Prof. Hußmann Informatik II (Maschinenwesen) Abstrakte Methoden • Methoden, für die keine Implementierung gegeben werden kann, sondern die ausschließlich als Platzhalter für speziellere Implementierungen dienen, werden "abstrakt" genannt. – Schlüsselwort "abstract" • Klassen, die abstrakte Methoden enthalten, heißen auch "abstrakte Klassen". • Beispiel: – Was ist die Implementierung der Methode "gibPreis" für die Oberklasse TBauteil? type TBauteil = class bauteilNr: string; function gibPreis: integer; virtual; abstract; end; Technische Universität Dresden Prof. Hußmann Informatik II (Maschinenwesen) Sichtbarkeit • Öffentlich = public • Privat = private – Sichtbar in der aktuellen Unit • Nur für Unterklassen = protected – Auch sichtbar in Unterklassen in einer anderen Unit • Geheimnisprinzip: – Grundsätzlich sollte so viel wie möglich der in einem Objekt gespeicherten Information als "privat" deklariert werden. – Erhöhung der Wartungsfreundlichkeit und Stabilität. Technische Universität Dresden Prof. Hußmann Informatik II (Maschinenwesen) Klassenstruktur für Bauteile-Beispiel (2) type TBauteil = class public bauteilNr: string; function gibPreis: integer; virtual; abstract; end; TElement = class(TBauteil) private preis: integer; public constructor Create(n: string; p: integer); function gibPreis: integer; override; end; TBaugruppe = class(TBauteil) private bauteile: array of TBauteil; public constructor Create(n: string); procedure bauteilHinzu (b: TBauteil); function gibPreis: integer; override; function gibAnzahl: integer; end; Technische Universität Dresden Prof. Hußmann Informatik II (Maschinenwesen) Implementierung Bauteile-Beispiel (1) constructor TElement.Create(n: string; p: integer); begin bauteilNr := n; preis := p; end; function TElement.gibPreis: integer; begin gibPreis := preis; end; constructor TBaugruppe.Create(n: string); begin bauteilNr := n; end; procedure TBaugruppe.bauteilHinzu(b: TBauteil); begin SetLength(bauteile,Length(bauteile)+1); bauteile[High(bauteile)] := b; end; Technische Universität Dresden Prof. Hußmann Informatik II (Maschinenwesen) Implementierung Bauteile-Beispiel (2) function TBaugruppe.gibPreis: integer; var i,p: integer; begin p := 0; for i:=0 to High(bauteile) do p := p + bauteile[i].gibPreis; gibPreis := p; end; function TBaugruppe.gibAnzahl: integer; var i,a: integer; begin a := 0; for i:=0 to High(bauteile) do if bauteile[i] is TBaugruppe then a := a + (bauteile[i] as TBaugruppe).gibAnzahl else a := a+1; gibAnzahl := a; end; Technische Universität Dresden Prof. Hußmann Informatik II (Maschinenwesen) Schichten-Architektur Bauteile_U.pas Benutzungsoberfläche Bauteile_Modell.pas Fachliches Modell Technische Universität Dresden Prof. Hußmann Informatik II (Maschinenwesen) Auszug aus "Bauteile_U.pas" procedure TForm1.AbfrageClick(Sender: TObject); var bauteilNr: string; b: TBauteil; begin if BauteilListBox1.ItemIndex <> -1 then begin bauteilNr := BauteilListBox1.items [BauTeilListBox1.ItemIndex]; b := findeBauteil(bauteilNr); BauteilPreis.Text := IntToStr(b.gibPreis); if b is TElement then BauteilAnzahl.Text := '(keine)' else BauteilAnzahl.Text := IntToStr((b as TBaugruppe).gibAnzahl); end end; Technische Universität Dresden Prof. Hußmann Informatik II (Maschinenwesen)