Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi DynArray: array of TShape (https://www.delphipraxis.net/10590-dynarray-array-tshape.html)

Char 21. Okt 2003 16:45


DynArray: array of TShape
 
Hallo,

bei mir kommt folgender Fehler, wenn ich ein dynamisches Array von TShape-Objekten machen möchte:

[Fehler] USCtrl.pas(11): Undefinierter Bezeichner: 'TShape'

Kann man keine Arrays von solchen Objekten machen? Wenn doch, wie?

RomanK 21. Okt 2003 16:47

Re: DynArray: array of TShape
 
Hoi,
wie wärs mit bissle Code ??

Alexander 21. Okt 2003 16:48

Re: DynArray: array of TShape
 
Füg mal die Unit "ExtCtrls" in die Uses-Klausel ein.

Char 21. Okt 2003 17:06

Re: DynArray: array of TShape
 
Ok, danke, das hat diesen Fehler schonmal beseitigt.

Ich habe (versucht) eine Klasse zu schreiben, die in der Lage ist (so ziemlich) beliebig viele TShape-Komponenten zu erzeugen und zu verwalten.

Sie enthält bis jetzt die Methoden
Delphi-Quellcode:
{ public-Teil }
  procedure Move(x,y: integer; whichOne: integer = 0); { <- Position eines einzelnen oder aller Shapes verändern }
  procedure Del(whichOne: integer = 0);     { <- Einzelne oder alle Shapes löschen }
  procedure Show(show: boolean = true; whichOne: integer = 0); { <- Einzelne oder alle Shapes anzeigen lassen }
  procedure Resize(width,height: integer; whichOne: integer = 0); { <- Größe eines einzelnen oder aller Shapes verändern }
  constructor Create(howMany,iWidth,iHeight: integer;            { <- constructor, dem die "Startwerte" übergeben werden }
                                      BrushStyle: TBrushStyle = bsSolid;
                                      PenStyle: TPenStyle = psSolid;
                                      ParentDing: TWinControl);  { <- ???}
  destructor Free; { <- destructor, der erst alle TShapes free't und dann die Klasse ansich }
Die TShapes werden in einem Array gespeichert:

Delphi-Quellcode:
{ private-Teil }
 FShapeArray: array of TShape;
In der Theorie sollte das funktionieren, doch ein paar Fehler sind noch drin.

Welchen Datentyp muss denn ParentDing im Constructor haben?

Der sieht übrigends su aus:

Delphi-Quellcode:
constructor TShapeControl.Create(howMany,iWidth,iHeight: integer;
                   BrushStyle: TBrushStyle = bsSolid;
                   PenStyle: TPenStyle = psSolid;
                   Parent: TWinControl); { <- ??? }
var i: integer;
begin
 SetLength(FShapeArray, howMany);

 for i:=0 to high(FShapeArray) do
    FShapeArray[i] := TShape.Create(self);

    with FShapeArray[i] do
     begin
      parent := ParentDing; { <- ParentDing }
      height := iHeight;
      width := iWidth;
      left  := 0;
      top   := 0;
      brush.style := BrushStyle;
      pen.style  := PenStyle;
     end;
end;
Ich hoffe ihr könnt mir helfen...

:dp:

Christian Seehase 21. Okt 2003 18:01

Re: DynArray: array of TShape
 
Moin Char,

als Parent solltest Du jedes WinControl nehmen können.
Eine Kompo wird immer relativ zum Parent dargestellt.

Char 22. Okt 2003 10:35

Re: DynArray: array of TShape
 
So, nun ist nurnoch ein Fehler drin:

[Fehler] USCtrl.pas(36): Inkompatible Typen: 'TComponent' und 'TSCtrl'

Und zwar im Constructor wenn die einzelnen Shape-objekte erstellt werden:

Delphi-Quellcode:
constructor TShapeControl.Create(howMany,iWidth,iHeight: integer;
                   BrushStyle: TBrushStyle = bsSolid;
                   PenStyle: TPenStyle = psSolid;
                   ParentObj: TWinControl = nil);
var i: integer;
begin
 SetLength(FShapeArray, howMany);

 for i:=0 to high(FShapeArray) do
  begin
    FShapeArray[i] := TShape.Create(self);    { <- Da ist der Fehler}

    with FShapeArray[i] do
     begin
      parent := ParentObj;
      height := iHeight;
      width := iWidth;
      left  := 0;
      top   := 0;
      brush.style := BrushStyle;
      pen.style  := PenStyle;
     end;
  end;
end;
Ich vermute, das ist, weil ich bei create(self) das self eben für meine Klasse TSCtrl steht, eigentlich aber etwas anderes sein sollte (ein TCOntrol eben).

Mein Problem ist, dass ich überhaupt nicht weiß, warum bei TShape.create überhaupt ein Parameter angegeben werden muss, was er zu bedeuten hat und wie er in meinem Fall aussehen müsste.

Genauso ist das beim Parameter meines Constructors ParentObj. Der muss nämlich einen Startwert haben. Ich hab einfach nil genommen, weil mir nichts besseres eingefallen ist. Am liebsten würde ich das so haben, dass als ParentObj standardmäßig das Hauptformular des Projektes genommen wird. Geht das?

Christian Seehase 22. Okt 2003 10:42

Re: DynArray: array of TShape
 
Moin Char,

von welcher Klasse ist denn TSCtrl abgeleitet?

Der Owner den Du bei TShape.Create angeben sollst dient in gewisser Weise zur Resourcenverwaltung.
Wird eine Komponente zerstört, die Owner für andere Objekte ist, werden diese auch zerstört.
Gibst Du als Owner nil an, so musst Du selber dafür sorgen, dass die Objekt wieder freigegeben werden.

Char 22. Okt 2003 12:26

Re: DynArray: array of TShape
 
TSCtrl ist selbst geschrieben (also dürfte dann von TObject abgeleitet sein).

Die Shapes werden sowieso selbst wieder zerstört, wenn das TSCtrl-Objekt gefreet wird oder fehlt da was?

Delphi-Quellcode:
destructor TShapeControl.Free;
var i: integer;
begin

 for i:=0 to high(FShapeArray) do
  FShapeArray[i].Free;

 SetLength(FShapeArray,0);

end;

Also muss ich beim constructor TShape.create(nil) machen.

Was muss nun als "Startwert" bei ParentObj stehen, damit TShape.parent gleich dem Hauptformular des Projektes ist? Oder geht nil da?

Christian Seehase 22. Okt 2003 13:00

Re: DynArray: array of TShape
 
Moin Char,

Zitat:

Zitat von Char
TSCtrl ist selbst geschrieben (also dürfte dann von TObject abgeleitet sein).

Dann vermute ich mal, dass Du

Delphi-Quellcode:
type
  TSCtrl = class
  //...
  end;
geschrieben hast. Das wäre dann identisch mit

Delphi-Quellcode:
type
  TSCtrl = class(TObject)
  //...
  end;
Den Destructor würde ich noch abwandeln:

Delphi-Quellcode:
destructor TShapeControl.Destroy;
var i: integer;
begin

for i:=0 to high(FShapeArray) do
  FShapeArray[i].Free;

SetLength(FShapeArray,0);
inherited;
end;
Das inherited ist zwar bei Ableitung von TObject nicht zwingend erforderlich, da es hier nichts zu erben gibt, aber besser man gewöhnt sich dran, dann kann man es nicht so leicht vergessen, wenn es mal erforderlich ist.

Bei dieser Konstruktion, bei der Du die Shapes selber wieder freigibst, kannst Du natürlich problemlos nil als Owner übergeben.

Wenn das Hauptformular der Parent sein soll, dann solltest Du einfach dieses als Parameter im constructor übergeben.

Char 22. Okt 2003 13:50

Re: DynArray: array of TShape
 
Delphi-Quellcode:
destructor TSCtrl.Destroy;
var i: integer;
begin

for i:=0 to high(FShapeArray) do
  FShapeArray[i].Free;

SetLength(FShapeArray,0);
inherited;
end;
Du nimmst also Destroy statt Free. Warum? Was ist der Unterschied zu Free? Ich kenn mich damit nicht aus (merkt man sicher ;) ), aber sollte man normalerweise nicht Free benutzen?

Was bedeutet/bewirkt inherited?

Dann noch eine Frage: Wenn das Programm beendet wird, werden dann alle Objekte automatisch destroyed oder freed oder muss ich den destructor von meiner Klasse selbst aufrufen? Ich gehe von letzterem aus.

Danke schonmal, es sind keine Fehler mehr zu finden. Nur eben diese Fragen noch.

Christian Seehase 22. Okt 2003 14:19

Re: DynArray: array of TShape
 
Moin Char,

der eigentliche destructor heisst i.d.R. Destroy.
Free führt noch Prüfungen durch, bevor es dann Destroy aufruft.

Die eigentliche Freigabe findet also in Destroy statt, das von Free aufgerufen wird.
Nennen kannst Du den natürlich wie Du willst, aber Free ist von seinem Sinn her vorbelegt, so dass es zumindest ungeschickt wäre den destructor so zu nennen. Zudem ist es Konvention einen destructor Destroy zu nennen (genauso wie der constructor üblicher Weise Create heisst.)

inherited heisst, dass die Methode gleichen Namens von der Basisklasse Deiner Klasse an dieser Stelle aufgerufen wird.
Gibst Du hier noch einen Namen mit (z.B. inherited MethodenName) so wird die Methode "MethodenName" der Basisklasse aufgerufen.
Ggf. sind dann noch Parameter zu übergeben.
Stimmt Deine Deklaration mit der der Basisklasse überein kannst Du den Namen und die Parameter weglassen, da diese automatisch übernommen werden.

BTW:
Ich hab' hier auch Blödsinn geschrieben: :oops: "Das inherited ist zwar bei Ableitung von TObject nicht zwingend erforderlich, da es hier nichts zu erben gibt, "
Man ruft ja den Destructor von TObject auf, und das sollte auch so sein.

Jetzt musst Du nur noch darauf achten, an welcher Stelle das inherited gesetzt wird.
Bei einem constructor sollte inherited als erstes aufgerufen werden, da es ja sinnvoll ist alles was von vorherigen Klassen stammt zu initialisieren, bevor man seine eigenen Ergänzungen initialisiert.
Aus dem gleichen Grunde gehört inherited beim Destructor ans Ende. Erst einmal die eigenen Sachen aufräumen, dann dass was aus vorhergehenden Klassen stammt.

Bei anderen Methoden die man überschreibt hängt es vom Zusammenhang ab, an welcher Stelle man das inherited einfügt. Meistens wohl als erstes, im Extremfall vielleicht auch gar nicht.

Zitat:

Zitat von Char
Wenn das Programm beendet wird, werden dann alle Objekte automatisch destroyed oder freed oder muss ich den destructor von meiner Klasse selbst aufrufen? Ich gehe von letzterem aus.

Windows sollte zwar in der Regel alles abräumen, was ein Prozess so an Resourcen belegt hat, aber es ist allemal sauberer dieses immer selber zu machen.

Char 22. Okt 2003 14:48

Re: DynArray: array of TShape
 
Also muss in meinen Constructor auch inherited ja?

Wieso funktioniert es denn dann auch ohne? TSCtrl ist ja eine Ableitung von TObject, wieso wird das Objekt ordnungsgemäß erstellt, wenn gar nicht die Create-Prozedur von TObject aufgerufen wird, sondern "nur" meine?

Und: Wieso geht inherited Free nicht? Free überprüft doch nur, ob das Objekt nicht vorher nil ist, weil Destroy alleine da nen Fehler ausgibt oder?

Fragen über Fragen...

[edit]

Wenn ich inherited in den Constructor schreibe kommt folgende Fehlermeldung:

[Fehler] USCtrl.pas(34): Inkompatible Typen

Und wenn ich den Destructor umschreibe kommt eine Warnung:
[Warnung] USCtrl.pas(22): Methode 'Destroy' verbirgt virtuelle Methode vom Basistyp 'TObject'

Was ist überhaupt eine virtuelle Methode (virtual)?
[/edit]

Christian Seehase 22. Okt 2003 15:43

Re: DynArray: array of TShape
 
Moin Char,

wenn Du es so schreibst

Delphi-Quellcode:
type
  TcsMyType = class(TObject)
  public
    constructor Create(const AsIrgendeinParameter : string);
    destructor Destroy; override;
  end;

implementation

{ TcsMyType }

constructor TcsMyType.Create(const AsIrgendeinParameter: string);
begin
  inherited Create;
  // ...
end;

destructor TcsMyType.Destroy;
begin
  //...
  inherited;
end;
sollte es gehen.

Beim inherited im constructor musst Du den Aufruf der Basisklasse so mitgeben, wie er in der Basisklasse deklariert ist, wenn Deine Deklaration davon abweicht.
Es wird vermutlich ohne inherited gehen, wenn in TObject.Create nichts initialisiert wird.

Zum Thema virtuelle Methoden kannst Du ja erst einmal in der Hilfe unter eben diesem Begriff nachschlagen. Ich finde, dass ist da ganz gut erklärt.
GGf. wäre es dann vielleicht sinnvoll mal einen neuen Thread aufzumachen, denn irgendwie sind wir hier, bezogen, auf Deine Eingangsfrage, schon lange OT ;-)


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:46 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz