| Autor |
Beitrag |
Nait
Hält's aus hier
Beiträge: 10
|
Verfasst: Di 31.08.10 22:31
Hallo liebe Foren-Mitglieder. Ich habe schon ein paar Mal über Google auf eurer Seite Rat gefunden und mich schließlich (wo ich ein nun nicht mehr so einfaches Problem habe) entschlossen hier anzumelden. Hurra.
Ich arbeite mit Delphi 2005 und habe mich heute an die OOP herangewagt. Ein sehr interessantes Thema, nur ist mein Lehrbuch für Turbo Pascal 8, 1991 herausgegeben und arbeitet noch mit object statt mit class. Ansonsten war es aber immer sehr hilfreich. (Ich habe das Buch erst seit wenigen Jahren, falls die Frage aufkommt <_<)
Im Verlauf des Tages hatte ich ein altes unterbrochenes Projekt missbraucht, um den ersten OOP-Testlauf zu machen und Delphi meinte beim Debuggen, einfach 3 Zeilen überspringen zu können und sich danach zu wundern, warum das Objekt nicht initialisiert wurde. Ich habe kurzerhand ein neues Projekt gestartet und die Entwicklung des ersten Objektes war schwierig, aber ich habs geschafft.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98:
| TSFE = class(TImage) public private FArt: Byte; Fright_loaded: Boolean; procedure Init(X, Y: SmallInt; PathToPic, PicName, PicExt: String; PicIndex: SmallInt; PicIndex0, Art: Byte); function Bild_laden(PathToPic, PicName, PicExt: String; PicIndex: SmallInt; PicIndex0: Byte): Boolean; function verschieben(strecke: SmallInt; Richtung: Byte): Boolean; procedure Done; protected end; ...
procedure TSFE.Init(X, Y: SmallInt; PathToPic, PicName, PicExt: String; PicIndex: SmallInt; PicIndex0, Art: Byte); begin FArt:= Art; Left:= X; Top:= Y; AutoSize:= true; if Bild_laden(PathToPic, PicName, PicExt, PicIndex, PicIndex0) then Fright_loaded:= true; end;
function TSFE.Bild_laden(PathToPic, PicName, PicExt: String; PicIndex: SmallInt; PicIndex0: Byte): Boolean; var path, nullen: String; z, zi, i: Integer; treffer: Boolean; begin if (PicIndex<0) or (PicIndex0=0) then Result:= false else begin path:= PathToPic + PicName; z:= 1; for i:= 1 to PicIndex0 do z:= 10*z; treffer:= false; i:= 1; zi:= 1; while (i <= PicIndex0) and not treffer do begin zi:= 10*zi; if PicIndex < zi then treffer:= true; inc(i); end; if not treffer then Result:= false else begin Result:= true; nullen:= ''; while zi < z do begin nullen:= nullen + '0'; zi:= 10*zi; end; path:= path + nullen + IntToStr(PicIndex) + '.' + PicExt; try Picture.LoadFromFile(path); except Result:= false; end; end; end; end;
function TSFE.verschieben(strecke: SmallInt; Richtung: Byte): Boolean; begin if strecke < 0 then Result:= false else begin Result:= true; case Richtung of 0: Top:= Top - strecke; 1: Left:= Left + strecke; 2: Top:= Top + strecke; 3: Left:= Left - strecke; else Result:= false; end; end; end;
procedure TSFE.Done; begin Free; end; |
eigentlich wollte ich was mit constructor Create(Parameter); virtual; statt dem Init machen, aber Delphi gab warnung, dass dadurch der TIMage-Create gestört werden würde, also hab ichs gelassen.
Dann hab ich den Erben gemacht:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| TObj = class(TSFE) private FPunkte: SmallInt; procedure Init(X, Y: SmallInt; PathToPic, PicName, PicExt: String; PicIndex: SmallInt; PicIndex0, Art: Byte; Punkte: SmallInt); end;
...
procedure TObj.Init(X, Y: SmallInt; PathToPic, PicName, PicExt: String; PicIndex: SmallInt; PicIndex0, Art: Byte; Punkte: SmallInt); begin FPunkte:= Punkte; TSFE.Init(X, Y, PathToPic, PicName, PicExt, PicIndex, PicIndex0, Art); end;
|
und bei "//Rest initialisieren" sagt Delphi mir "Diese Form des Methodenaufrufes ist nur für Klassenmethoden erlaubt." Ich verstehe die Aussage, aber nicht, warum das so ist. Meine Frage wäre jetzt, wie ich das ganze umgestalten kann, damits funktioniert.
Ich wäre für Hilfe sehr dankbar. Vielleicht kann ich mich mal revanchieren (man, das sieht falsch geschrieben aus <_<)
PS: Ist Vorbereitung für ein Pacman-Game
PPS: Die Seite hier hab ich schon durch.
EDIT: Dass TPaintBox besser ist, wurde zur Kenntnis genommen.
Zuletzt bearbeitet von Nait am Mi 01.09.10 08:48, insgesamt 1-mal bearbeitet
|
|
elundril
      
Beiträge: 3747
Erhaltene Danke: 123
Windows Vista, Ubuntu
Delphi 7 PE "Codename: Aurora", Eclipse Ganymede
|
Verfasst: Di 31.08.10 22:59
Hallo und  im Delphi-Forum!
Ich denke du musst bei der TFSE mal hinter dem hier ein virtual anhängen:
Delphi-Quelltext 1: 2: 3:
| procedure Init(X, Y: SmallInt; PathToPic, PicName, PicExt: String; PicIndex: SmallInt; PicIndex0, Art: Byte) |
Damit weiß das dass die abgeleitete Klasse diese Methode überschreiben darf. Damit musst du in der abgeleiteten Klasse ein override hinten anfügen. Damit du dann die Methode auch wirklich überschreibst.
Weiters darfst du in der abgeleiteten Klasse nicht TFSE.Init aufrufen sondern musst einfach ein inherited; statt dem TFSE.Init schreiben. Damit sagst du das der Compiler den Code der Elter-Klasse ausführen soll.
Falls du noch Literatur in Sachen OOP brauchst, einiges steht auch in Christian's Crashkurs und im Delphi-Wikibook.
lg elundril
_________________ This Signature-Space is intentionally left blank.
Bei Beschwerden, bitte den Beschwerdebutton (gekennzeichnet mit PN) verwenden.
Für diesen Beitrag haben gedankt: Nait
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mi 01.09.10 03:46
Nait hat folgendes geschrieben : | Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| procedure TObj.Init(X, Y: SmallInt; PathToPic, PicName, PicExt: String; PicIndex: SmallInt; PicIndex0, Art: Byte; Punkte: SmallInt); begin FPunkte:= Punkte; TSFE.Init(X, Y, PathToPic, PicName, PicExt, PicIndex, PicIndex0, Art); end; |
und bei "//Rest initialisieren" sagt Delphi mir "Diese Form des Methodenaufrufes ist nur für Klassenmethoden erlaubt." |
Du hast einmal die Klasse als Bauplan für Objekte von dem Typ und dann tatsächlich erzeugte Objekte. Hier versuchst du Init bei dem Bauplan aufzurufen.
Das ist aber genauso als würdest du versuchen die Haustür bei dem Bauplan eines Architekten zu öffnen statt bei dem danach gebauten Haus.
Deshalb kommt, dass das nur bei Klassenmethoden geht. Denn es gibt auch von einem konkreten Objekt unabhängige Methoden, die dann aber auch nicht auf in einem Objekt erst gespeicherte Daten zugreifen können. Diese heißen Klassenmethoden. Das aber nur am Rande.
Ja, was du machen willst ist doch Init der Elternklasse aufrufen. Das geht so: Delphi-Quelltext 1:
| inherited Init(X, Y, PathToPic, PicName, PicExt, PicIndex, PicIndex0, Art); | Dann zum Konstruktor:
Der bekommt bei TImage wie bei jeder VCL-Komponente einen Owner. Wenn du deine neue Komponente auf ein Formular legen können willst, dannn wird dabei dieser Konstruktor aufgerufen, der genau nur diesen Owner bekommt. In diesem Fall ist eine solche Init-Methode passend, weil du keinen Konstruktor mit anderen Parametern aufrufen kannst.
Wenn du selbst die Komponente erzeugst, kannst du auch andere Parameter einführen. Dann wäre es am besten Owner: TComponent als ersten Parameter zu benutzen. Die Warnung, dass der alte Konstruktor verdeckt wird oder sowas ist richtig, lässt sich aber umgehen.
Du kannst dem Konstruktor ein overload anhängen. Damit sagst du, dass dieser Konstruktor mit deinen Parametern zusätzlich zur Verfügung steht. So bleibt der alte erreichbar. Wennn du den alten gar nicht mehr nutzen möchtest, kannst du reintroduce anhängen. Damit sagst du Delphi, dass du den Konstruktor neu einführst und der alte dir egal ist.
Du kannst diesen alten Konstruktor dann in deinem erreichen: Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| constructor Create(Owner: TComponent; X: Integer); overload;
...
constructor ... begin inherited Create(Owner); FX := X; end; | Für ein Spiel wiederum eignet sich TImage allerdings nicht so gut. Da wäre eine TPaintBox besser.
Für diesen Beitrag haben gedankt: Nait
|
|
Nait 
Hält's aus hier
Beiträge: 10
|
Verfasst: Mi 01.09.10 08:46
danke erstmal für die Begrüßung rasche Antwort! Der Hinweis mit inherited war elementar, da hätt ich wohl ewig gesucht. Übrigens wusste ich was Klassenmethoden sind und deshalb auch, dass ich die nicht wollte xD
Okay, wenn es funktioniert, sieht es so aus:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31:
| TSFE = class(TImage) private ... procedure Init(X, Y: SmallInt; PathToPic, PicName, PicExt: String; PicIndex: SmallInt; PicIndex0, Art: Byte);
... end;
TObj = class(TSFE) private FPunkte: Byte; procedure Init(X, Y: SmallInt; PathToPic, PicName, PicExt: String; PicIndex: SmallInt; PicIndex0, Art: Byte; Punkte: SmallInt); end;
...
procedure TSFE.Init(Parameter); begin ... end;
procedure TObj.Init(Parameter); begin FPunkte:= Punkte; inherited Init(X, Y, PathToPic, PicName, PicExt, PicIndex, PicIndex0, Art); end; |
nun kommen folgende Varianten (in der Typvereinbarung von TSFE und TObj) mit Meldungen von Delphi. Achja, die beiden Deklarationen stehen immer noch im jeweiligen private-Teil
Delphi-Quelltext 1: 2: 3:
| procedure Init(Parameter); virtual; ... procedure Init(Parameter); | -> nichts, aber da später ja noch weitere Erben kommen, sollte TObj.Init auch virtual sein...
Delphi-Quelltext 1: 2: 3:
| procedure Init(Parameter); virtual; ... procedure Init(Parameter); virtual; | -> Warnung: "Methode Init verbirgt virtuelle Methode vom Basistyp TSFE"
Delphi-Quelltext 1: 2: 3:
| procedure Init(Parameter); virtual; ... procedure Init(Parameter); override; virtual; | -> Fehler: "Deklaration von Init unterscheidet sich von vorheriger Deklaration" "Felddefinition nicht erlaubt nach Methoden der Eigenschaften"
Delphi-Quelltext 1: 2: 3:
| procedure Init(Parameter); virtual; ... procedure Init(Parameter); virtual; override; | -> Warnung: "Methode Init verbirgt virtuelle Methode vom Basistyp TSFE" Fehler: "Felddefinition nicht erlaubt nach Methoden der Eigenschaften"
Anscheinend wende ich eure Informationen falsch an -.-
Die overload-Lösung für den Konstruktor behalte ich im Hinterkopf, geplant war aber eher eine Vererbungslösung wie mit den beiden Init-Prozeduren. Es ist an sich also dasselbe Problem.
Dass TPaintBox besser geeignet sein soll, hab ich schon an anderer Stelle gelesen, ich werde mich aber erst später damit auseinander setzen.
Die beiden Links werd ich mir später anschauen, jetzt geh ich mich erstmal aus der Kirche austragen Verdammter Mittwoch, hätte nen Termin machen sollen -.-
Ja, ich schaus mir jetzt an.
EDIT: Moment, das hier reicht?
Delphi-Quelltext 1: 2: 3:
| procedure Init(Parameter); virtual; ... procedure Init(Parameter); override; | -> Fehler: "Deklaration von Init unterscheidet sich von vorheriger Deklaration" Hm, hatte es gerade ähnlich hier gelesen... Hier ist vllt. nochmal anzumerken, dass TObj.Init einen Parameter mehr als TSFE.Init hat.
Zuletzt bearbeitet von Nait am Mi 01.09.10 09:10, insgesamt 2-mal bearbeitet
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mi 01.09.10 09:03
Die Methode ist bereits virtual, beim Überschreiben mit override gehört das nicht mehr hin. 
|
|
Nait 
Hält's aus hier
Beiträge: 10
|
Verfasst: Mi 01.09.10 10:08
aso, wenn ich also Init in TObj erneut deklariere, denkt er sich virtual dazu! Trotzdem mag er das Override nicht. Aber ich denke, ich muss die Methoden gar nicht unbedingt virtual gestalten oder?
Hab übrigens inzwischen TMovObj gemacht:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84:
| TMovObj = class(TObj) FTimer: TTimer; private FState, FFrame, FItv_laenge: Byte; FTimer_running: Boolean; procedure Init(X, Y: SmallInt; PathToPic, PicName, PicExt: String; PicIndex: SmallInt; PicIndex0, Art, State: Byte; Punkte: SmallInt; Itv_laenge: Byte; Itv_dauer: Cardinal); procedure Run; procedure OnTime(Sender: TObject); procedure Stop; procedure Done; end;
...
procedure TMovObj.Init(X, Y: SmallInt; PathToPic, PicName, PicExt: String; PicIndex: SmallInt; PicIndex0, Art, State: Byte; Punkte: SmallInt; Itv_laenge: Byte; Itv_dauer: Cardinal); begin inherited Init(X, Y, PathToPic, PicName, PicExt, PicIndex, PicIndex0, Art, Punkte); FState:= State; FFrame:= 0; FItv_laenge:= Itv_laenge; FTimer.Interval:= Itv_dauer; FTimer_running:= false; end;
procedure TMovObj.Run; begin FTimer.OnTimer:= OnTime; FTimer_running:= true; end;
procedure TMovObj.OnTime(Sender: TObject); begin verschieben(FItv_laenge,FState); end;
procedure TMovObj.Stop; begin FTimer.OnTimer:= nil; FTimer_running:= false; end;
procedure TMovObj.Done; begin if FTimer_running then Stop; FTimer.Free; inherited Done; end;
procedure TForm1.FormCreate(Sender: TObject); begin DoubleBuffered:= true; MovObj1:= TMovObj.Create(Self); MovObj1.Parent:= Self; MovObj1.FTimer:= TTimer.Create(Self); MovObj1.Init(20,30,'C:\','Kirby','bmp',0,4,1,1,200,10,100); end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin MovObj1.Done; end;
procedure TForm1.Button1Click(Sender: TObject); begin if MovObj1.FTimer_running then MovObj1.Stop else MovObj1.Run; end; |
Ich finds lustig, Bewegung auf Knopfdruck  Konnte ich auch schon vorher gestalten, aber so find ich es i-wie einfacher. Achja, der Kirby ist aus nem anderen Projekt mit inzwischen 5 Units, die ohne OOP auskommen mussten ;_;
Btw, ist es günstiger, den Timer über Enabled zu stoppen, anstatt die Prozedur-Zuweisung zu ändern?
EDIT: Noch ne Frage, wenn ich später Methoden schreibe wie
function schnitt2Einheiten(Einh1, Einh2: TSFE): Boolean;kann ich dann auch sowas
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| var Obj1: TObj; MovObj1, MovObj2: TMovObj;
... begin schnitt2Einheiten(MovObj1,MovObj2); schnitt2Einheiten(MovObj1,Obj1); | machen, da TObj von TSFE und TMovObj von TObj abgeleitet sind?
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 02.09.10 06:10
Nait hat folgendes geschrieben : | | aso, wenn ich also Init in TObj erneut deklariere, denkt er sich virtual dazu! |
Die Methode ist dann ja bereits virtual.
Was im Hintergrund passiert ist, dass virtuelle Methoden an einer anderen Stelle liegen, die stehen in einer Tabelle (die Virtual Method Table). Darüber wird dann beim Überschreiben die neue eingefügt. Wenn eine Methode nicht virtuell ist und da drin steht, kannst du sie nicht überschreiben.
Ja, wenn da ein zusätzlicher Parameter drin ist, musst du overload nehmen. Weil du eine Methode mit dem selben Namen, aber anderen Parametern hinzufügst. Auch reintroduce sollte gehen, ich kann es im Moment nicht testen.
Zur reineditierten Frage:
Ja, das geht.
Für diesen Beitrag haben gedankt: Nait
|
|
|