Autor Beitrag
Tino
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Veteran
Beiträge: 9839
Erhaltene Danke: 45

Windows 8.1
Delphi XE4
BeitragVerfasst: Di 06.05.03 16:25 
Pointer

Autor: Motzi
E-Mail: motzi_84@aon.at

Tutorial zum Downloaden


Inhalt:
    1.0 - Pointer
    1.1 - Was sind Pointer?
    1.2 - Wozu Pointer ?
    1.3 - Die Arbeit mit Pointers
    1.3.1 - Der @-Operator
    1.3.2 - Der ^-Operator
    2.0 - Der Bezug Objekte-Pointer
    2.1 - Die Situation in "Old-Pascal"
    2.2 - Die Situation in Delphi
    2.3 - Die Hintergründe
    3.0 - Das Programm
    3.1 - Die Vorbereitung
    3.2 - Das Erzeugen
    3.3 - Das Anzeigen/Verstecken
    3.4 - Das Löschen
    3.5 - Das Beenden




1.0 - Pointer

Da dieses Programm auf Pointers aufbaut hier zuerst einmal eine allgemeine Einführung in die Arbeit mit Pointer. Wer mit diesem Bereich schon vertraut ist kann diesen Teil überspringen.

1.1 - Was sind Pointer?

Im 32-bittigen Windows hat jedes Programm seinen eigenen 2 GB großen Adressraum (eigentlich sinds 4 GB, allerdings verwendet Windows 2 GB in eigener Regie. Das Programm hat daher nur Zugriff auf die unteren 2 GB) In diesem Adressraum werden alle Objekte (z.B.: Komponenten, ...) und Variablen gespeichert. Allerdings muss man auf diese auch zugreifen können, und das funktioniert mit sogenannten Pointers oder Zeigern. Ein Pointer ist also eigentlich nichts anderes als eine Integer-Variable in der eine Adresse gespeichert ist, also ein Zeiger auf diese Adresse. An dieser Adresse können nun Daten abgelegt werden oder auch schon liegen.

Angenommen wir haben einen Schrank der jetzt den Speicher repräsentiert. In diesem Schrank haben wir 100 Schubladen und an jeder Schublade wurde eine Nummer angebracht, also von 1 bis 100. Diese Nummern alle möglichen Adressen für diesen Speicher (den Schrank) Genauso wie eine Nummer angibt in welcher Lade sich die benötigten Daten befinden, genauso kann man sich den Zugriff über einen Pointer vorstellen. Die Nummer gibt nur die Lade an, nicht was sich darin befindet. Ein Pointer gibt nur die Adresse im Speicher an, nicht was sich dahinter verbirgt.

1.2 - Wozu Pointer ?

Diese Frage wird immer wieder gestellt... Wozu sind Pointer überhaupt gut? Die Verwendung von Zeigern ist aus mehreren Gründen sinnvoll. Sie sind für das Verständnis der Sprache Object Pascal wichtig, da sie in einem Programm oft hinter den Kulissen agieren und nicht explizit auftreten. Zeiger werden von allen Datentypen verwendet, die große, dynamisch zugewiesene Speicherblöcke benötigen. Beispielsweise sind lange String-Variablen ebenso wie Klassenvariablen implizite Zeiger. In vielen komplexen Programmierkonstrukten ist die Verwendung von Zeigern unverzichtbar.

In vielen Situationen sind Zeiger die einzige Möglichkeit, die strikte Typisierung der Daten durch Object Pascal zu umgehen. Du kannst z.B. die in einer Variablen gespeicherten Daten ohne Berücksichtigung ihres Typs verarbeiten, indem du sie über den Allzweckzeiger Pointer referenzieren, diesen in den gewünschten Typ umwandeln und ihn anschließend wieder dereferenzieren. (die Begriffe referenzieren und dereferenzieren werden weiter unten erklärt) Außerdem werden in vielen Algos und Programmen ganze Blöcke verschoben und herumkopiert. Wenn man sich mit Pointern auskennt erspart man sich das und verbiegt einfach die Zeiger ein bisschen. D.h. dass statt kilobyte schweren Blöcken nur die 4 Byte des Pointers kopiert werden, und das das schneller geht sollte doch jedem einleuchten.

1.3 - Die Arbeit mit Pointers

1.3.1 - Der @-Operator

Der @-Operator liefert die Adresse an der eine Variable zu finden ist, d.h. er erzeugt einen Zeiger auf seinen Operanden. z.B.:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
var
  p: Pointer;
  i: Integer;
begin
  i := 1;
  p := @i; {weißt dem Pointer p die Adresse zu an der die Integer-Variable i gespeichert ist}
end;

Der @-Operator kann auch im Zusammenhang mit Funktionen Prozeduren oder Methoden auftauchen, allerdings möchte ich nicht näher darauf eingehen, da dies in diesem Programm nicht verwendet wird. Wer Lust hat kann das ja in der Delphi-Hilfe nachlesen.

Hinweis: Der @-Operator kann nicht im Zusammenhang mit Objekt-Eigenschaften verwendet werden! Eine Objekt-Eigenschaft ist nur eine Schnittstelle über die indirekt auf eine intern verwaltete Variable zugegriffen werden kann. Eine Objekt-Eigenschaft ist also keine Variable in diesem Sinne und hat daher auch keine eigene Adresse. Und auf die privaten Variablen hat man keinen Zugriff!

1.3.2 - Der ^-Operator

Der ^-Operator referenziert bzw. dereferenziert einen Pointer, erfüllt also 2 Funktionen. Er kann vor einem Typbezeichner stehen, z.B.:
ausblenden Delphi-Quelltext
1:
^Typname					

In diesem Fall bezeichnet der Operator einen Typ, der Zeiger auf Variablen des Typs Typname darstellt. Der Typ Typname wird referenziert.

Das Symbol ^ kann aber auch auf eine Zeigervariable folgen:
ausblenden Delphi-Quelltext
1:
Zeiger^					

In diesem Fall dereferenziert der Operator den Zeiger, d.h. er liefert den Wert an der Speicheradresse, die der Zeiger angibt.

Beispiel:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
var   X, Y: Integer; // X und Y sind Integer-Variablen
  P: ^Integer; // P zeigt auf einen Integer
begin
  X := 17// Einen Wert an X zuweisen
  P := @X; // Adresse von X an P zuweisen
  Y := P^; // P dereferenzieren; Ergebnis an Y zuweisen
end;

Hier nochmal eine genauere Erklärung zu den einzelnen Zeilen:

P: ^Integer;

Hier wird eine Variable deklariert, die einen Pointer auf einen Integer darstellt. Dem Compiler wird klar gemacht, dass es sich bei dieser Variable um einen Pointer handelt und dass an der Adresse, auf die dieser Pointer zeigt eine Integer-Variable liegt.

P := @X;

Wie schon oben beschrieben bekommt man durch den @-Operator die Adresse einer Variable. Diese Adresse kann nun einer Pointer-Variable (egal ob das jetzt der "Allzweck-Pointer" Pointer ist, oder irgendein Pointer der einen andren Objekt-Typ referenziert) zugewiesen werden. Der Pointer zeigt dann auf die ihm zugewiesene Adresse. Da in diesem Bsp. X eine Integer-Variable ist und P ein Pointer der eine Integer-Vraible referenziert, kann man nach dieser Zuweisung P ohne Probleme dereferenzieren und man bekommt den Inhalt der Integer-Variable auf die der Pointer zeigt.

Y := P^;

Hier wird der Pointer P dereferenziert und der dadurch bekommene Wert wird Y zugewiesen. Dereferenzieren bedeutet, dass man auf das Objekt, auf das der Pointer zeigt, zugreift. Ein dereferenzierter Pointer verhält sich genauso wie eine normale Variable von der Klasse die der Pointer referenziert. Damit ein Pointer dereferenziert werden kann, muss er ein Objekt referenzieren. Das ist zwingend notwendig, da der Compiler erst mit dieser Referenzierung den "Bauplan" des Objektes mit auf den Weg bekommt. Erst dadurch weiß der Compiler um was für ein Objekt es sich da handelt auf das der Pointer zeigt und wie es gehandhabt werden muss.

Wenn jetzt jedem klar ist, dass das obige Bsp. im Prinzip genau dasselbe macht wie die Zuweisung Y := X hat dieser Teil seinen Zweck erfüllt!

Hinweis: Der Allzwecktyp Pointer kann nicht dereferenziert werden. Er muss zuvor in einen anderen Pointertyp umgewandelt werden!

Der @- bzw der ^-Operator sind also genau das Gegenteil voneinander und heben sich damit auf. Die Verwendung von @IrgendeinPointer^ ist also ident mit der Verwendung von IrgendeinPointer.

Das war es dann auch schon. Wenn ihr euch diese Seite durchgelesen habt, habt ihr hoffentlich das Wesentliche bereits verstanden. Bevor wir jetzt zum Programm kommen noch kurz was über Objekte.

2.0 - Der Bezug Objekte-Pointer

Viele werden sich jetzt vielleicht denken, was Objekte mit Pointers/Zeigern zu tun haben.. mehr als Delphi vermuten lässt!

2.1 - Die Situation in "Old-Pascal"

Also.. damit man vielleicht den Hintergrund ein bisschen besser versteht mal ein Code aus alten Pascal Zeiten:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
type
  PBeispiel = ^TBeispiel;
  TBeispiel = object(TObject)
    ID: Integer;
    constructor Init;
  end;
var
  aBsp: PBeispiel;
begin
  aBsp := New(PBeispiel, Init);
  aBsp^.ID := 5;
  Dispose(aBsp);
end;

Was passiert hier?

PBeispiel = ^TBeispeil;
TBeispiel = object(TObject)


Es wird ein neues Objekt TBeispiel deklariert und ein Pointer PBeispiel der ein TBeispiel-Objekt referenziert.

aBsp := New(PBeispiel, Init);

Hier wird ein Objekt vom Typ TBeispiel erzeugt und der Pointer auf diese wird aBsp zugwiesen.

aBsp^.ID := 5;

Da wir nur einen Pointer auf das Objekt haben müssen wir diesen dereferenzieren wenn wir auf das Objekt bzw. dessen Eigenschaften zugreifen wollen.

Dispose(aBsp);

Hiermit geben wir das Objekt über dessen Pointer wieder frei.

2.2 - Die Situation in Delphi

Und hier das ganze nochmal aus "Delphi-Sicht":
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
type
  TBeispiel = class(TObject)
    ID: Integer;
    constructor Create;
  end;
var
  aBsp: TBeispiel;
begin
  aBsp := TBeispiel.Create;
  aBsp.ID := 5;
  aBsp.Free;
end;

Ich hoffe diese Zeilen sind allen klar.

2.3 - Die Hintergründe

Borland Pascal

Der Unterschied sollte jedem sofort auffallen! Ein Objekt unter Borland Pascal muss einen Zeigertyp explizit deklarieren. Um eine Instanz dieses Objekts, ist der Aufruf der Funktion New notwendig, wobei New den "Bauplan" des Objekts via Parameter mit auf den Weg bekommt. Auf Objektfelder (Eigenschaften, Methoden) dieses Objektes darf erst nach der Dereferenzierung zugegriffen werden und auch für das Entsorgen der Instanz ist eine globale Prozedur, Dispose zuständig.

Delphi

Ganz anders - und vor allem einfacher - sieht die Situation in Delphi aus. Zum einen wird kein spezieller Zeigertyp für das Objekt benötigt, da die Instanzvariable für das Objekt vom gleichen Typ ist wie das Objekt selbst. Auf Objektfelder (Eigenschaften, Methoden) wird direkt - also ohne Dereferenzierung - zugegriffen, obwohl die Instanzvariable nur ein Zeiger auf die Objektinstanz ist. Der Unterschied zum Pascal-Bsp. ist, dass Delphi diese Objekte implizit dereferenziert und du gar nichts davon mitbekommst. Allerdings verbirgt dieser Mechanismus auch etwas, was vielen Anfängern und Unwissenden nicht bwusst ist. Wir haben gerade gehört, Objekte sind Pointer, also gilt für diese Zuweisung:
ausblenden Delphi-Quelltext
1:
Object1 := Object2					

Es werden keine Objektfelder kopiert! Stattdessen wird Object1 der Wert von Object2 zugewiesen und der Wert eines Objektes ist nunmal der Pointer auf dieses. Daher bekommt Object1 die Adresse von Object2. Und da jetzt beide Objecte (Pointer) auf dieselbe Adresse zeigen sind die beiden Object ident, da sie eigentlich nur ein Objekt kapseln.

Das gefährliche dabei ist, dass wenn ein Unwissender eine solche Zuweisung in seinem Programm verwendet und beide Objekte instanziert sind geht der eine Pointer verloren (in obigem Bsp. eben der von Object1) und es entsteht ein Speicherleck. Sofern man nicht noch ein andres Objekt/Pointer hat, das auf auf diese Adresse zeigt hat man keine Möglichkeit mehr auf das Objekt zuzugreifen und den Speicher freizugeben!

PS: Jedes Delphi-Objekt führt intern eine Variable Self mit, mit der auf das besitzende Objekt zugreifen kann.

3.0 - Das Programm

Wie man z.B. bei ICQ sehen kann, kann man ein Fenster auch mehrmals anzeigen. Da aber jedes Fenster unabhängig von den anderen Fenstern sein soll, braucht jedes seine eigene Adresse. Dies ließe sich auf zwei Arten lösen. Eine Möglichkeit wäre einen Dynamischen Array zu nehmen, die zweite eben mit Pointers zu arbeiten, wobei diese Methode flexibler ist. (Wenn jemand meint mit einem Array wäre es einfacher, dann wünsche ich ihm viel Spaß beim ausprobieren!)

3.1 - Die Vorbereitung

Also, für unser Programm brauchen wir zwei Formen. Eine Hauptform (bei mir MainForm genannt) und eine Form die wir zur Laufzeit öfters erzeugen wollen (bei mir MultipleForm). Auf der MainForm befinden sich diverse Buttons zum Erzeugen, Löschen und Anzeigen und außerdem noch eine Listbox in der wir dann die Liste der Formulare anzeigen. (Das Label auf der MultipleForm ist nur optional um zu zeigen, das jedes Fenster unabhängig von den anderen ist)

Aufgrund der ungarischen Notation ist es üblich alle Objektdefinitionen mit einem großen T zu beginnen (z.B. TForm, TButton, TEdit, ...). Daher werden Pointer eben mit einem großen P definiert.
ausblenden Delphi-Quelltext
1:
type PMultipleForm = ^TMultipleForm; { --> referenziert einen Zeiger auf ein Objekt des Typs TMultipleForm}					

Bei einem Klick auf den CreateButton soll also eine Form erzeugt und in die Liste eingefügt werden, nur wie? Alles was wir brauchen ist eine TList die wir bereits zu Beginn (im OnCreate-Ereignis) erzeugen.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
procedure TMainForm.FormCreate(Sender: TObject);
begin
  FormListe := TList.Create; // --> erzeugt eine Liste
end;

Der Typ TList verwaltet eine dynamische Liste mit Pointers.

3.2 - Das Erzeugen

Nun geht es ans Erzeugen. Also, gehen wir langsam das OnClick-Ereignis des Create Button durch.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure TMainForm.CreateButtonClick(Sender: TObject); begin
  GetMem(FormP, SizeOf(TMultipleForm)); { --> reserviert an der Adresse von FormP den Speicher für die Form}
  FormP^ := TMultipleForm.Create(Self); // --> erzeugt die Form
  FormP^.Caption := FormP^.Caption + IntToStr(Liste.Items.Count + 1);
  FormP^.Label1.Caption := LabelText.Text;
  FormP^.OnHide := ListeClick; { --> beim verstecken des Fensters muss ja die Button-Aufschrift aktualisiert werden...}  FormListe.Add(FormP); // --> fügt den Pointer zur Liste hinzu
  Liste.Items.Add('Form' + IntToStr(Liste.Items.Count + 1));
end;


GetMem(FormP, SizeOf(TMultipleForm));
FormP ist ein Zeiger auf ein Objekt des Typs TMultipleForm. Nun muss an dieser Adresse aber noch der Speicherplatz reserviert werden den ein TMultipleForm-Objek belegt, und das geschieht eben mit GetMem.

FormP^ := TMultipleForm.Create(Self);

Mit FormP^ wird der Pointer FormP dereferenziert, also auf das Objekt auf das er zeigt zugegriffen und mit TMultipleForm.Create die neu erzeugte Form als das Objekt von FormP im Speicher abgelegt.

FormP^.Caption := FormP^.Caption + IntToStr(Liste.Items.Count + 1);
FormP^.Label1.Caption := LabelText.Text;
FormP^.OnHide := ListeClick; { --> beim verstecken des Fensters
muss ja die Button-Aufschrift aktualisiert werden...}


Diese Zeilen sollten sich von selbst erklären.

FormListe.Add(FormP);

Die Form Liste vom Typ TList verwaltet alle Pointer an denen unsere Formen gespeichert sind, also müssen wir den Pointer dazugeben, damit wir nachher wissen wo die Form gespeichert ist und auf sie zugreifen können.

3.3 - Das Anzeigen/Verstecken

So, jetzt geht's ans Anzeigen.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure TMainForm.ShowHideClick(Sender: TObject);
begin
  FormP := FormListe[Liste.ItemIndex];
  FormP^.Visible := not FormP^.Visible;
  if FormP^.Visible then
    ShowHide.Caption := 'Verstecken'
  else
    ShowHide.Caption := 'Anzeigen';
end;

Sollte eigentlich allen klar sein was da passiert.

3.4 - Das Löschen

Irgendwann will man die Formen sicher wieder löschen, also schauen wir uns folgendes an.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure TMainForm.DelButtonClick(Sender: TObject);
begin
  FormP := FormListe[Liste.ItemIndex];
  FormP^.Free; { --> löscht die Informationen der Form aus dem Speicher, Speicher bleibt aber belegt!!}
  FreeMem(FormP, SizeOf(TMultipleForm)); { --> gibt den Speicher der für die Adresse reserviert wurde frei}
  FormListe.Delete(Liste.ItemIndex);
  Liste.Items.Delete(Liste.ItemIndex);
end;


FormP^.Free;

Normalerweise kümmert sich bei einem Aufruf von Free Delphi darum, dass alles gelöscht und der Arbeitsspeicher wieder freigegeben wird, hier ist es allerdings nicht so. Nach dem Aufruf von Free sind zwar die Informationen über die Form aus dem Arbeitsspeicher verschwunden, aber wir haben ja auch Platz für den Pointer reserviert. (Siehe 2.2 - Das Erzeugen) Also müssen wir den auch freigeben.

FreeMem(FormP, SizeOf(TMultipleForm));

Mit dem Aufruf von FreeMem wird exakt der Speicher den wir am Anfang mit GetMem an der Adresse von FormP reserviert haben wieder freigegeben

3.5 - Das Beenden

Beim Beenden einer Anwendung wird zwar der gesamte Inhalt des Adressbereiches gelöscht, aber man sollte es sich trotzdem zur Angewohnheit machen, alle manuell erzeugten Objekte auch selbst wieder freizugeben.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
var
  i: Integer;
begin
  for i := 0 to FormListe.Count-1 do
  begin
    FormP := FormListe[i];
    FormP^.Hide; 
    { --> muss zuerst aufgerufen werden, da mit dem Aufruf von Free 
     auch das OnHide-Ereignis aufgerufen wird.}

    { da sich nach dem Aufruf von OnHide der Pointer verändert haben 
      könnte würde ein EAccessViolent entstehen}

    FormP := FormListe[i];
    FormP^.Free;
    FreeMem(FormP, SizeOf(TMultipleForm)); 
   { --> gibt den Speicher der für die Adresse reserviert wurde frei}
  end;
  FormListe.Free;
end;

Wenn man den Rest verstanden hat sollte das auch klar sein.
Das war es auch schon. Im Source-Code sind zwar noch ein paar Feinheiten enthalten, aber wenn das alles klar ist sollten die auch kein Problem darstellen.
Balmung der blaue Gott
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 52

WinXP

BeitragVerfasst: So 05.02.06 16:21 
user profile iconTino hat folgendes geschrieben:
Im 32-bittigen Windows hat jedes Programm seinen eigenen 2 GB großen Adressraum (eigentlich sinds 4 GB, allerdings verwendet Windows 2 GB in eigener Regie. Das Programm hat daher nur Zugriff auf die unteren 2 GB)


Der Adressraum ist, soviel ich weiß, der Gesamte virtuelle Speicher. Aber der kann doch nicht größer als 4GB sein. Wenn ich jetzt 3 Programme parallel laufen lasse ergäbe sich Insgesamt ein Adressraum von 12GB!!!
Du meintest doch, dass allen Programmen zusammen ein 2GB Adressraum zur Verfügung steht, oder?

So ich lese jetzt erst mal weiter, cu.
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: So 05.02.06 17:48 
user profile iconBalmung der blaue Gott hat folgendes geschrieben:
Der Adressraum ist, soviel ich weiß, der Gesamte virtuelle Speicher. Aber der kann doch nicht größer als 4GB sein. Wenn ich jetzt 3 Programme parallel laufen lasse ergäbe sich Insgesamt ein Adressraum von 12GB!!!
Du meintest doch, dass allen Programmen zusammen ein 2GB Adressraum zur Verfügung steht, oder?

Nein, das stimmt schon so..! Deswegen heißt es ja "virtueller" Speicher, er existiert nicht wirklich sondern nur virtuell. Jeder Prozess hat einen 4GB Adresssraum, der gesamte Adressraum und damit alle Adressen sind aber virtuell, haben mit der Adresse im Arbeitsspeicher überhaupt nix zu tun. Intern mappt der VMM (Virtual Memory Manager) von Windows die Daten vom virtuellen Adressraum in den realen Arbeitsspeicher (bzw eigentlich virtuellen Speicher da ja über die Swap-Datei noch zusätzlicher Arbeitsspeicher simuliert wird).
Der virtuelle Adressraum eines Prozesses ist also im Prinzip nur der "Blickwinkel" des Prozesses - er sieht 4GB frei adressierbaren Speicher und seine Daten darin, wo diese Daten aber im Arbeitsspeicher liegen kann ihm egal sein, das managed Windows komplett transparent. Außerdem sieht er nur die eigenen Daten und nicht auch die eines anderen Prozesses -> Sicherheit, der Prozess kann nicht einfach so die Daten eines andren Prozesses manipulieren. Aber auch wenn ein Prozess einen 4GB großen Adressraum zur Verfügung hat, so gibt es dennoch kaum Programme die diese 4GB auch wirklich voll ausnutzen...

Gruß, Motzi

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!
Udontknow
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2596

Win7
D2006 WIN32, .NET (C#)
BeitragVerfasst: Mi 15.02.06 14:39 
Hallo!

Ein paar Anmerkungen:

In dem Quellcode für die Erstellung eines neuen Formulars wird das Eintragen in die Formliste weit hinten in der Quellcode-Zeile hinter einem anderen Befehl ausgeführt. Mach doch lieber einen Umbruch, das verwirrt sonst sehr.

Wieso wird in dem Programmbeispiel mit den vielfach instanziierten Formularen eigentlich so umständlich mit einem Pointer auf eine Form-Variable und Pointertypen gearbeitet? Warum denn einen Pointer auf einen Pointer speichern, und nicht den Pointer (also die Form-Variable) selber?

So ist es doch viel übersichtlicher:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
procedure TMainForm.CreateButtonClick(Sender: TObject); 
var NewForm:TMultipleForm;
begin  
  NewForm := TMultipleForm.Create(Self); 
  NewForm.Caption := NewForm.Caption + IntToStr(Liste.Items.Count + 1);  
  NewForm.Label1.Caption := LabelText.Text;  
  NewForm.OnHide := ListeClick; 
  FormListe.Add(NewForm); 
  Liste.Items.Add('Form' + IntToStr(Liste.Items.Count + 1));  
end

procedure TMainForm.DelButtonClick(Sender: TObject);  
var FormToDelete:TMultipleForm;
begin  
  FormToDelete := TMultipleForm(FormListe[Liste.ItemIndex]); //Typecast von Pointer zu TMultipleForm  
  FormToDelete.Free; //Formular-Objekt wird freigegeben, Speicher des Objekts wird frei
  FormListe.Delete(Liste.ItemIndex);  //Pointer wird entfernt, Speicher des Pointers wird frei.
  Liste.Items.Delete(Liste.ItemIndex);  
end;


Ausserdem wäre noch ein Verweis auf die tolle TObjectlist schön, die einem auch viel Arbeit wie das Freigeben abnehmen kann (siehe Eigenschaft OwnsObjects).

Cu, :)

Andreas
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Mi 15.02.06 14:56 
user profile iconUdontknow hat folgendes geschrieben:
Wieso wird in dem Programmbeispiel mit den vielfach instanziierten Formularen eigentlich so umständlich mit einem Pointer auf eine Form-Variable und Pointertypen gearbeitet? Warum denn einen Pointer auf einen Pointer speichern, und nicht den Pointer (also die Form-Variable) selber?

Weil das Tutorial schon uralt ist und ich die Verwendung von Pointern und dyn. alloziertem Speichern demonstrieren wollte, damals aber keine vernünftige Idee dafür hatte. ;)
Inzwischen gibt es sowieso schon ein komplett neu überarbeitetes Pointer-Tutorial mit einer neuen Demo (implementieren einer Double-Linked-List) unter www.manuel-poeter.de

Gruß, Motzi

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!