Zitat von
Hador:
Danke das habe ich gebraucht.
Jetzt sehe ich klarer.
Man sollte sich aber über die Auswirkungen vom Verdecken der Methoden bewust sein.
Da können sich leicht Fehler in ein Programm einschleichen, die man dann manchmal lange suchen muss
Zitat von
Hador:
EDIT: Evt. verstehe ich das ja, wenn ihr mir mal 'n praktisches Beispiel vorwerft.
Unterschiede zwischen Überschreiben und Verdecken
Wenn in einer Methodendeklaration dieselben Bezeichner- und Parameterangaben wie bei einer geerbten Methode ohne die Anweisung override angegeben werden, wird die geerbte Methode durch die neue Deklaration verdeckt. Beide Methoden sind jedoch in der abgeleiteten Klasse vorhanden, in der die Methode statisch gebunden wird.
Bei T2 werden Compilerwarnungen ausgegeben, das die Methoden verdeckt sind.
Mithilfe der Anweisung
reintroduce kann verhindert werden, dass der Compiler Warnungen ausgibt, wenn eine zuvor deklarierte virtuelle Methode verdeckt wird, aber das Verhalten ist trotzdem das Selbe.
Unterschiede zwischen virtuellen und dynamischen Methoden
Virtuelle und dynamische Methoden sind von der Semantik her identisch. Sie unterscheiden sich nur bei der Implementierung der Aufrufverteilung zur Laufzeit. Virtuelle Methoden werden auf Geschwindigkeit, dynamische Methoden auf Code-Größe optimiert.
Im Allgemeinen kann mit virtuellen Methoden polymorphes Verhalten am effizientesten implementiert werden. Dynamische Methoden sind hilfreich, wenn in einer Basisklasse eine große Anzahl überschreibbarer Methoden deklariert ist, die von vielen abgeleiteten Klassen geerbt, aber nur selten überschrieben werden.
Delphi-Quellcode:
type
T1 = class(TObject)
procedure A; virtual;
procedure B; dynamic;
procedure C;
end;
T2 = class(T1)
procedure A; // Methoden sind neu deklariert, aber nicht überschrieben
procedure B;
procedure C;
end;
T3 = class(T1)
procedure A; override; // Methoden sind überschrieben
procedure B; override;
// procedure C; override; // Fehlermeldung, es können keine statischen Methoden überschrieben werden
end;
...
var
Obj1: T1;
Obj2: T2;
Obj3: T3;
begin
// das ist klar
Obj1 := T1.Create;
Obj1.A; // T1.A wird aufgerufen
Obj1.B; // T1.B wird aufgerufen
Obj1.C; // T1.C wird aufgerufen
Obj1.Free;
// das ist klar
Obj2 := T2.Create;
Obj2.A; // T2.A wird aufgerufen
Obj2.B; // T2.B wird aufgerufen
Obj2.C; // T2.C wird aufgerufen
Obj2.Free;
// das ist klar
Obj3 := T3.Create;
Obj3.A; // T3.A wird aufgerufen
Obj3.B; // T3.B wird aufgerufen
Obj3.C; // T1.C wird aufgerufen, da nicht überschrieben
Obj3.Free;
// Achtung: Das ist oft nicht gewünscht
Obj1 := T2.Create;
Obj1.A; // T1.A wird aufgerufen
Obj1.B; // T1.B wird aufgerufen
Obj1.C; // T1.C wird aufgerufen
T2(Obj1).A; // T2.A wird aufgerufen
T2(Obj1).B; // T2.B wird aufgerufen
T2(Obj1).C; // T2.C wird aufgerufen
Obj1.Free;
// Achtung: So sollte es (meistens) sein
Obj1 := T3.Create;
Obj1.A; // T3.A wird aufgerufen
Obj1.B; // T3.B wird aufgerufen
Obj1.C; // T1.C wird aufgerufen, da nicht überschrieben
Obj1.Free;
end;