Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Verwenden von Pointern (https://www.delphipraxis.net/172360-verwenden-von-pointern.html)

Getox 28. Dez 2012 15:49

Delphi-Version: 5

Verwenden von Pointern
 
Hiho,

Ich habe da so ein winziges Problem. Ich habe beim onCreate von Form1 ein Objekt gebaut. Dieses wird am ende onDestroy auch wieder freigegeben. Nun möchte ich dieses eine Objekt Global in allen Forms verwenden. Dabei ist es wichtig, dass es das selbe Objekt ist und nicht ein neues der gleichen Art.

Ich habe mir gedacht dass ich onCreate halt dieses Objekt erstelle, danach einen Pointer auf dieses setze und den Pointer an alle anderen Forms weitergebe, so dass ich in allen Forms mit dem selben Objekt arbeite.

Jetzt habe ich direkt eine ganze Latte Probleme.

- geht das überhaupt so wie ich mir das so vorstelle?
- ich hab zwar schon einen Pointer-Guide durchgelesen, aber weis irgendwie trotzdem nicht, wie ich das schreiben sollte.
- Kann ich die einzelnen Methoden des Objektes auch über den Pointer ansprechen (Pointername.Methode) oder muss ich mir irgendwie ein Objekt initialisieren und dem dann mein altes Objekt zuweisen, so dass ich mehrere Objekte initialisiert habe, aber im Grunde alle Objekte nur eines sind. (Nicht einfach zu erklären, was ich für einen Gedanken hatte...)

Kann mir vielleicht jemand ein kleines Beispiel entwerfen, das ich dann analysieren kann? Das würde mich sehr freuen.

Gruß Getox

Medium 28. Dez 2012 15:57

AW: Verwenden von Pointern
 
Vergiss Pointer hierfür ab besten gleich wieder. Die Variable, der du das Objekt bei der Erstellung zuweist ist bereits intern ein Pointer, der von Delphi aber etwas komfortabler als "Referenz" nutzbar gemacht wird.

Delphi-Quellcode:
var
  foo, bar: TMyClass;
begin
  foo := TMyClass.Create;
  bar := foo;
end;
foo und bar sind danach 2 Referenzen auf exakt das selbe Objekt.

Edit: Bei Records schaut die Welt wieder etwas anders (komplizierter) aus, aber wenn es wirklich und nur um Objekte geht, spielen "manuelle" Pointer keine Rolle.

Klaus01 28. Dez 2012 15:59

AW: Verwenden von Pointern
 
Hallo,

estelle Dir eine Unit mit der "globalen Klasse".
Diese Unit kannst Du in alle Forms einbinden.
Nutze das Singleton Pattern.
Delphi-Quellcode:
type
  TDeineKlasse = class(TObject)
    private
      class var fInstance: TDeineKlasse;
      constructor create;
   public
      class function getInstance: TDeineKlasse;
  end;


implementation

class function TDeineKlasse.getInstance: TDeineKlasse;
begin
  if not assigned(fInstande) then
    fInstance := TDeineKlasse.create;

  result := fInstance;
end;
Grüße
Klaus

stahli 28. Dez 2012 16:05

AW: Verwenden von Pointern
 
Ein Objekt wird immer über einen Pointer angesprochen.
Wenn Du z.B. auf Panel1 zugreifst, wird auf eine Speicheradresse zugegriffen.
Der entsprechende Speicherplatz auf den der Pointer verweist wird dann in als Klasse TPanel interpretiert.

Insofern kannst Du Deine Instanz Panel1 anderen Units zugänglich machen und musst nicht extra mit Pointern hantieren.

Wenn Du der Unit uForm2 Deine Unit uForm1 bekannt gibst, kannst Du auf Form1.Panel1 ganz normal zugreifen.

Du könntest aber auch eine globale Unit definieren, in der Du eine entsprechende Objektvariable definierst.
Im OnCreate Deines Hautpformulars weist Du dieser dann Deine Objektinstanz zu und im OnDestroy setzt Du sie auf nil.
Dann kannst Du von den anderen Formularen aus auf diese globale Variable zugreifen.

Im Grunde läuft es darauf raus, dass Du eine Variable von verschiedenen Stellen aus zugängliche Variable definierst, der dann irgendwann die eigentliche Objektinstanz zugewiesen wird.
Eigentlich funktioniert das genau wie mit einer Integer- oder String-Variable.

Es gibt noch andere und bessere Lösungen, aber so kann man das schon auch realisieren.

Medium 28. Dez 2012 16:11

AW: Verwenden von Pointern
 
Zitat:

Zitat von stahli (Beitrag 1196976)
Eigentlich funktioniert das genau wie mit einer Integer- oder String-Variable.

Eigentlich genau nicht, da nach so etwas hier dann verschiedene Werte in den Variablen stecken:

Delphi-Quellcode:
var
  str1, str2: String;
  int1, int2: Integer;
  obj1, obj2: TMyClass;
begin
  str1 := 'hallo';
  str2 := str1;
  str1 := 'bye';
  // hiernach ist str1 = 'bye' und str2 = 'hallo'

  int1 := 1;
  int2 := int1;
  int1 := 2;
  // hiernach ist int1 = 2 und int2 = 1

  obj1 := TMyClass.Create;
  obj1.SomeProperty := 1;
  obj2 := obj1;
  obj1.SomeProperty := 2;
  // hier hingegen sind obj1.SomeProperty und obj2.SomeProperty = 2
end;
Edit: Der Vollständigkeit halbar das ganze noch mit einem Record:
Delphi-Quellcode:
var
  rec1, rec2: TMyRec;
begin
  rec1.SomeValue := 1;
  rec2 := rec1;
  // bis hier hin sind rec1 und rec2 IDENTISCH, nicht nur gleich. Beide Variablen greifen auf den SELBEN Speicher zu
  rec2.SomeValue := 2;
  // Und hier greift dann compiler magic (copy-on-write genannt): Es wurde im Hintergrund eine Kopie angelegt. Nunmehr zeigt nur noch rec1 auf den anfänglichen Speicher, rec2 hat automatisch eine völlig eigene Instanz erhalten.
end;
Mit Strings läuft das übrigens eigentlich genau so ab.

Getox 28. Dez 2012 16:17

AW: Verwenden von Pointern
 
Was ich eigentlich vorhabe ist, dass ich in den Units in denen sich Formulare befinden so weinig wie möglich Code habe.

Mein Hauptprogramm habe ich eigentlich in einer Unit ohne Formular. Dieses habe ich als Klasse geschrieben. Ich will nun onCreate von Form1 eine instanz dieser Klasse erstellen, auf Welche dann alle Forms zugreifen. Dreh- und Angelpunkt ist somit also nicht mehr die unit mit Form1, sondern mein Objekt.

Innerhalb dieses Objektes sind dann halt verschiedenste methoden und Variablen (unter anderem auch ein record) auf die aber alle forms irgendwann dann mal zugreifen.

Inwiefern das nun sinnvoll ist, kann ich nicht einschätzen, aber auf jeden Fall übt es mich im Umgang mit der Objektorientierung.

Wie ich mein Problem nun löse: Ich schreibe einfach in allen Units, wo es erforderlich ist meine Unit, in der ich das Objekt erstellt habe in USES. Dann kann ich das ja direkt über Form1.Core ansprechen, egal in welcher Unit ich dann bin :D

Aber danke für die vielen Vorschläge, das hat mich wieder etwas schlauer gemacht.

stahli 28. Dez 2012 16:29

AW: Verwenden von Pointern
 
Dein Ansatz ist sehr sinnvoll, da Du so eine gute Trennung zwischen Businesslogik und GUI erhältst. :thumb:

Schau Dir mal weiter obden den Hinweis zum Singleton an.
Damit kannst Du immer genau eine Instanz einer Klasse sicher stellen.

Wie Du es genau realiseren kannst hängt auch von Deiner Delphiversion ab und ob Di die Instanz Deiner Klasse schon zur Designtime benötigst, z.B. um irgendwelche Einstellungen im Objketinspektor vorzunehmen.

Du könntest z.B. in der Unit Deiner Klasse eine öffentliche Variable MyObjekt definieren und dieser in Deinem Constructor "Self" zuweisen und im Destructor nil.
Sofern Du nur eine Instanz Deiner Klasse erzeugst, würde das funktionieren.

Bjoerk 28. Dez 2012 16:45

AW: Verwenden von Pointern
 
Ich würde den Constructor der entsprechenden Forms überschreiben (die Klasse als Parameter mitschicken), also diese Forms nicht von der dpr-Datei verwalten lassen.

Delphi-Quellcode:
type
  TFormA = class(TForm)
   ..
  private
    FKlasse: TKlasse;
  end;

..

procedure TFormA.FormCreate(Sender: TObject);
begin        
  FKlasse:= TKlasse.Create;
  FormB:= TFormB.Create(Self, FKlasse);
end;

procedure TFormA.FormDestroy(Sender: TObject);
begin
  FKlasse.Free;
  FormB.Free;
end;
Delphi-Quellcode:
type
  TFormB = class(TForm)
    ..
  private
    FKlasse: TKlasse;
  public
    constructor Create(AOwner: TComponent; const Klasse: TKlasse); reintroduce; overload;
  end;

constructor TFormB.Create(AOwner: TComponent; const Klasse: TKlasse);
begin
  inherited Create(AOwner);
  FKlasse:= Klasse;
end;

hoika 28. Dez 2012 21:59

AW: Verwenden von Pointern
 
Hallo,

warum jetzt wieder die Klasse im Form ???
Eine globale Unit mit Singleton !

Heiko

Bjoerk 28. Dez 2012 22:50

AW: Verwenden von Pointern
 
Zitat:

Zitat von hoika (Beitrag 1197009)
Hallo,
warum jetzt wieder die Klasse im Form ???
Eine globale Unit mit Singleton !
Heiko

Aha. Und wieso erklären wir dann in jedem 3. TE, so wenig global wie möglich? BTW, das IST ein Singleton. :wink:

Klaus01 29. Dez 2012 09:23

AW: Verwenden von Pointern
 
..ein Singleton hat den Charme, dass davon nur eine Instanz erstellen kann.
Von einer globalen Klasse können jede Menge Instanzen erstellt werden.

Grüße
Klaus

Furtbichler 29. Dez 2012 10:38

AW: Verwenden von Pointern
 
Zitat:

Zitat von Klaus01 (Beitrag 1196974)
Nutze das Singleton Pattern.
[DELPHI]
type
TDeineKlasse = class(TObject)
...

Bei meinem älteren Delphi (BDS 2006) hindert mich niemand daran, beliebig viele Instanzen der TDeineKlasse mit dem 'Create'-Konstruktor zu erstellen (obwohl der Konstruktor ja als privat deklariert ist).

Ich würde für ein Singleton eher so vorgehen:
Delphi-Quellcode:
type
   TSingleton = Class
   private
       class var fInstance: TSingleton;
       class var fRefCnt : Integer;
       class function NewInstance: TObject; Override;
       procedure FreeInstance; override;
    End;
implementation

{ TSingleton }

procedure TSingleton.FreeInstance;
begin
  if fRefCnt=1 then begin
    inherited FreeInstance;
    fInstance:=Nil;
  end;
  dec (fRefCnt);
end;

class function TSingleton.NewInstance: TObject;
begin
  if fInstance=Nil then
    fInstance := TSingleton(inherited NewInstance);
  Result := fInstance;
  inc(fRefCnt);
end;
end.
Dann muss ich gar nicht wissen, das das ein Singleton ist, sondern kann mir immer, wenn ich es brauche, eine Instanz erstellen. Die Objektlogik mappt das ja auf die eine Instanz. Zudem habe ich so keine Möglichkeit, mehrere Instanzen zu erstellen.

Ich persönlich halte das für sauberer, als umständlich über 'GetInstance' auf die globalen Daten zuzugreifen.

Bjoerk 29. Dez 2012 11:41

AW: Verwenden von Pointern
 
Alternative: Wenn die Forms nur modal angezeigt werden, könnte auch jede Form ihre eigene Instanz haben. Hierzu die Klasse von TPersitent ableiteten und in Settern die jeweils aktuellen Werte mit Assign kopieren.

Furtbichler 29. Dez 2012 14:45

AW: Verwenden von Pointern
 
Zitat:

Zitat von Bjoerk (Beitrag 1197047)
Alternative: Wenn die Forms nur modal angezeigt werden, könnte auch jede Form ihre eigene Instanz haben. Hierzu die Klasse von TPersitent ableiteten und in Settern die jeweils aktuellen Werte mit Assign kopieren.

Das ist aber von hinten durch die Brust ins Auge.

Bjoerk 29. Dez 2012 15:37

AW: Verwenden von Pointern
 
Zitat:

Zitat von Furtbichler (Beitrag 1197059)
Das ist aber von hinten durch die Brust ins Auge.

Würde ich einem Singleton ggf. dennoch vorziehen. Hast du mal Code mit Singletons gewartet? I can tell you.. Notfalls höchstens meine Variante #8.

Furtbichler 29. Dez 2012 19:41

AW: Verwenden von Pointern
 
Wo ist der unterschied zwischen einem 'zwangsweise' einmalig erzeugtem Objekt und einem, das per Konvention nur einmal erstellt wird (Form1, Application etc.)?

Beispiel: Ein Datenmodul wird nur einmal instantiiert und verwendet, obwohl es problemlos mehrere Instanzen geben könnte. Eine Singleton-Klasse dagegen sorgt dafür, das man nicht mehrere Instanzen erstellen *kann*.

Unterm Strich kommt jedoch das Gleiche heraus und 'zu warten' gibt es da nicht viel.

PS: Ich habe deine Variante (#8) vor einiger Zeit mal umgesetzt: Für mich war das sehr umständlich und auch unnötig.

Da ist mir meine Variante (die das Singleton-Verhalten komplett kapselt) allemal lieber. Dies ist auch gängige Praxis in anderen OOP-Sprachen. Ich kenne das z.B. von log4net (ein Logger für C#): Dort, wo man einen Logger benötigt, holt (=instantiiert) man sich mal eben einen. Ob das ein Singleton ist oder nicht, weiß ich nicht und es interessiert mich auch nicht.

Erkläre mir doch mal, was Du unter 'ein Singleton-Objekt warten' verstehst.

stahli 29. Dez 2012 20:46

AW: Verwenden von Pointern
 
Ich würde auch nicht zwanghaft an einem bestimmten (übertriebenen) Prinzip festhalten.

Die einfachste funktionierende Lösung ist die beste.
Wenn ich ein Framework baue, das in vielen Projekten verwendet werden soll, ist ggf. eine etwas allgemeinere Lösung mit mehr Implementierungsaufwand notwendig.

Wenn es (wie ich Getox verstanden habe) nur um die Instanziierung eines BL-Objektes geht, kann das im Initialisierungsabschnitt der Unit oder im Mainform erledigt werden, ohne dass man dadurch irgendwelche Probleme erhält.
Der Entwickler weiß ja selbst, ob und wann er eine Instanz erzeugt.

Manchmal tun mir die Neueunsteiger wirklich leid wenn sie eine einfache Lösung suchen und dann hier mit Grundsatzdebatten erschlagen werden... :wink:

jaenicke 29. Dez 2012 21:46

AW: Verwenden von Pointern
 
Zitat:

Zitat von Klaus01 (Beitrag 1196974)
estelle Dir eine Unit mit der "globalen Klasse".
Diese Unit kannst Du in alle Forms einbinden.

Da fehlt aber noch die Freigabe, am einfachsten via class destructor (der wird nach ggf. dem finalization Abschnitt der Unit aufgerufen). Und der Konstruktor wird Create geschrieben, nicht create. Zudem sieht für mich eine entsprechende Property schöner aus:
Delphi-Quellcode:
type
  TDeineKlasse = class(TObject)
  private
    class var
      FInstance: TDeineKlasse;
    constructor Create;
    class function GetInstance: TDeineKlasse; static;
  public
    class destructor Destroy;
    class property Instance: TDeineKlasse read GetInstance;
  end;

implementation

class function TDeineKlasse.GetInstance: TDeineKlasse;
begin
  if not Assigned(FInstande) then
    FInstance := TDeineKlasse.Create;

  Result := FInstance;
end;

class destructor TDeineKlasse.Destroy;
begin
  if Assigned(FInstance) then
    FInstance.Free;
end;

stahli 29. Dez 2012 22:01

AW: Verwenden von Pointern
 
Ab wann gibt es im Delphi eigentlich Klassenvariablen und Klassenkonstruktoren?

Die Funktionalität ist die gleiche wie eine Realisierung im Initialization- und Finalization-Abschnitt einer Unit.
Ältere Delphi-Versionen können das nicht (also evtl. vom TE nicht verwendbar).

Vielleicht ist es auch für das Verständnis eines Delphi-Neulings nicht immer unbedingt zweckmäßig.

Furtbichler 29. Dez 2012 22:05

AW: Verwenden von Pointern
 
Zitat:

Zitat von stahli (Beitrag 1197096)
Manchmal tun mir die Neueunsteiger wirklich leid wenn sie eine einfache Lösung suchen und dann hier mit Grundsatzdebatten erschlagen werden... :wink:

:oops:

jaenicke 29. Dez 2012 22:33

AW: Verwenden von Pointern
 
Zitat:

Zitat von stahli (Beitrag 1197102)
Ab wann gibt es im Delphi eigentlich Klassenvariablen und Klassenkonstruktoren?

Klassenvariablen gibt es seit Delphi 2006, aber mit einem Compilerfehler, der dazu führte, dass ein Zugriff von außerhalb der Unit knallte. Klasseneigenschaften gibt es glaube ich seit Delphi 2007 (?). Und Klassenkonstruktoren und -destruktoren gibt es seit Delphi 2010.

Getox 2. Jan 2013 10:35

AW: Verwenden von Pointern
 
Ich hab zwar bei der Hälfte von dem was ihr hier redet keine Ahnung was ihr meint, oder worum es eigentlich geht, aber ich habe mir Singleton mal angeschaut.

Lustigerweise gehen fast alle Threads über singleton in die Richtung: "Globale Variablen böse, Singleton böse, alles böse". Ich habe hier im Forum aber einen sehr gut verständlichen Guide als PDF gefunden und habe es damit verstanden und auch schon angewendet. Für meinen Zweck scheint es wirklich gut zu sein.

Ich verstehe zwar nicht was an globalen Variablen so "Böse" ist, aber egal... Mein alter Programmierlehrer hat immer gesagt, globale Variablen seinen einfach nur unschön. In Foren habe gelesen, dass globale Variablen "unangenehme Nebenwirkungen" haben könnten. Aber ich glaube das steht auf einem anderen Blatt.

Mein problem, wegen dem ich diesen Thread ursprünglich gestartet habe ist gelöst und dafür danke ich.

Und ja, ich habe schon öfters miterlebt, dass aus eine simplen Anfängerfrage riesige Grundsatzdiskussionen werden und das ist in der Tat sehr verwirrend für den fragenden Neuling. Wer hat jetzt recht? Was soll man jetzt tun? Was bedeutet eigentlich die Hälfte von dem was da geschrieben wurde?

Getox

BUG 2. Jan 2013 11:11

AW: Verwenden von Pointern
 
Zitat:

Zitat von Getox (Beitrag 1197334)
Ich verstehe zwar nicht was an globalen Variablen so "Böse" ist, aber egal... Mein alter Programmierlehrer hat immer gesagt, globale Variablen seinen einfach nur unschön. In Foren habe gelesen, dass globale Variablen "unangenehme Nebenwirkungen" haben könnten. Aber ich glaube das steht auf einem anderen Blatt.

Das ist alles genau das Gleiche. Das unschöne / böse an globalen Variablen ist, dass sie Fehler durch Seiteneffekte provozieren.
Durch das Verpacken in eine Klasse (Singletone) kann man das besser kontrollieren, die Gefahr von Seiteneffekten besteht aber weiterhin.

Wenn man den Gedanken der Seiteneffektfreiheit weiter verfolgt, kommt man irgendwann bei einer funktionalen Programmiersprache an. Was in anderen Programmiersprachen gute Praxis ist, wurde dort sprachlich geregelt. IMHO sollte jeder mal den funktionale Programmierstiel angesehen/praktiziert haben.


Ganz kommt man um die globalen Objekte aber auch nicht herum. Ein typisches Beispiel sind die Standard-Ein-/Ausgabe-Streams für die Konsole. Die gibt es halt nur einmal.

jaenicke 2. Jan 2013 11:39

AW: Verwenden von Pointern
 
Zitat:

Zitat von Getox (Beitrag 1197334)
Ich verstehe zwar nicht was an globalen Variablen so "Böse" ist, aber egal...

Dass man ihnen nicht ansieht wer wann etwas hineinschreibt, wer sie initialisiert, ...

Singletons mögen auch nicht schön sein, aber es ist zumindest klar, dass sie initialisiert sind, wenn man sie irgendwo benutzt. :wink:


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:29 Uhr.

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