![]() |
Record zurücksetzen (löschen)
Delphi-Quellcode:
Hi,
// Beispiel:
type TMyRecord = record Test_1: string; Test_2: string; Test_3: string; Integer_1: integer; Integer_2: integer; end; ... MyRecord: TMyRecord; da ich bei meiner Suche nichts gefunden habe, ahne ich, dass ich da selbst Hand angelegt muss. Aber dennoch die Frage: Gibt es eine Delphi-Funktion um ein Record zu löschen? Also sowas wie MyRecord := nil? :gruebel: Guido. Edit: Ein Demo der Lösung, für die ich mich entschieden habe, befindet sich in ![]() . |
Re: Record zurücksetzen (löschen)
Die Delphi-Hilfe:
Code:
[edit=Luckie]Neu abgschickt. Mfg, Luckie[/edit]
Records (erweiterte)
Zusätzlich zu den traditionellen Record-Typen lässt die Delphi-Sprache komplexere und “klassenähnliche” Record-Typen zu. Zu den Feldern können Records Eigenschaften und Methoden (einschließlich Konstruktoren), Klasseneigenschaften, Klassenmethoden, Klassenfelder und verschachtelte Typen haben. Weitere Informationen hierzu finden Sie in der Dokumentation zu Klassen und Objekten. Im Folgenden finden Sie eine Beispiel-Record-Typdefinition mit einigen “klassenähnlichen” Merkmalen. Obwohl Records nun einige der Merkmale von Klassen besitzen, gibt es wichtige Unterschiede zwischen Klassen und Records. Records unterstützen keine Vererbung. Records können variante Teile enthalten; Klassen nicht. Records sind Wertetypen, daher werden Sie bei der Zuweisung kopiert, per Wert übergeben und dem Stack zugewiesen, wenn sie nicht als global definiert sind oder explizit mit den Funktionen New und Dispose zugewiesen werden. Klassen sind Referenztypen, daher werden Sie bei der Zuweisung nicht kopiert, per Referenz übergeben und dem Heap zugewiesen. Records ermöglichen das Überladen von Operatoren auf Win32- und .NET-Plattformen; Klassen ermöglichen das Überladen von Operatoren nur bei .NET. Records werden automatisch mit einem Standardkonstruktor ohne Argumente erzeugt, Klassen dagegen müssen explizit erzeugt werden. Weil Records einen argumentlosen Standardkonstruktor haben, muss jeder benutzerdefinierte Record-Konstruktor ein oder mehr Parameter haben. Record-Typen können keine Destruktoren haben. Virtuelle Methoden (die mit den Schlüsselwörtern virtual, dynamic und message angegeben werden) dürfen in Record-Typen nicht verwendet werden. Im Gegensatz zu Klassen können Record-Typen auf der Win32-Plattform keine Schnittstellen implementieren; auf der .NET-Plattform können Records jedoch Schnittstellen implementieren. |
Re: Record zurücksetzen (löschen)
Warum schrummpelt den die Forum-Fensterdarstellung bei mir so zusammen?
Zum Thema: Ist da jetzt schon eine Antwort auf meine Frage drin? Dann bitte ein wenig genauer eingrenzen. Danke, Guido. |
Re: Record zurücksetzen (löschen)
Zitat:
Zitat:
|
Re: Record zurücksetzen (löschen)
Hallo, du könntest Finalize + FillChar verwenden, um deinen Record zu "löschen":
Delphi-Quellcode:
Oder du verwendest einen "BlankRecord". Vorteil: Du bestimmst selber die Werte, welche die Felder erhalten sollen.Finalize(MyRecord); FillChar(MyRecord, SizeOf(MyRecord), 0);
Delphi-Quellcode:
const
MyBlankRecord : TMyRecord = (Test_1: ''; Test_2: ''; Test_3: ''; Integer_1: 0; Integer_2: 0); begin MyRecord := MyBlankRecord; end; |
Re: Record zurücksetzen (löschen)
Was heißt den löschen?
|
Re: Record zurücksetzen (löschen)
Alle Felder leeren wahrscheinlich.
|
Re: Record zurücksetzen (löschen)
Man könnte den ersten Vorschlag von toms sogar noch etwas erweitern:
Delphi-Quellcode:
In der Methode Clear müssen vor dem abschließenden FillChar-Befehl alle (dynamischen) Daten im Record freigegeben werden. Finalize selbst berücksichtigt nur die referenzgezählten Daten. Speicherbereiche, die mit New o.ä. angelegt wurden, sowie Objekte, die durch Aufruf eines Konstruktors erzeugt wurden, muss man also auch manuell freigeben.
type
TMyRecord = record Test_1 : string; Test_2 : string; Test_3 : string; Integer_1 : Integer; Integer_2 : Integer; procedure Clear; end; procedure TMyRecord.Clear; begin Finalize (Self); FillChar (Self, SizeOf(Self), 0); end; // Anwendung: var MyRecord : TMyRecord; begin MyRecord.Clear; end; Gruß Hawkeye |
Re: Record zurücksetzen (löschen)
Mist an FillChar habe ich nicht gedacht.
|
Re: Record zurücksetzen (löschen)
Wenn er mit FillChar das Record löscht, dann werden aber die "Strings" da drin nicht wirklich überschrieben, sondern nur die Pointer, oder seh ich das jetzt falsch?
|
Re: Record zurücksetzen (löschen)
Zitat:
|
Re: Record zurücksetzen (löschen)
Mh nee.. Ich glaube das siehst du richtig. Jetzt wo du's sagst... Muss man halt wirklich ne Record-Methode (seltsame Teile wie ich finde..) schreiben
Zitat:
|
Re: Record zurücksetzen (löschen)
@Michael:
Danke für das "Entschrummpeln" der Forumsansicht! :thumb: Zitat:
Zitat:
Was ich vorhabe: Zum einen will ich hierbei meinen Delphi-Horizont erweitern. Bisher habe ich bei Records eine Routine geschrieben, in der alle Felder auf einen definierten Wert gesetzt wurden. Beispiel:
Delphi-Quellcode:
Nun will ich zum Einen wissen, ob es da eine einfachere Möglichkeiten gibt, um meinen Code kürzer und übersichtlicher zu halten.Type TMyRecord = record Test_1 : string; Test_2 : string; Integer_1 : Integer; Integer_2 : Integer; end; function PrepareMsg(AMyRecord: TMyRecord): string; begin Result := 'Test_1: ' + AMyRecord.Test_1 + #13#10+ 'Test_2: ' + AMyRecord.Test_2 + #13#10+ 'Integer_1: ' + IntToStr(AMyRecord.Integer_1) + #13#10+ 'Integer_2: ' + IntToStr(AMyRecord.Integer_2); end; procedure ResetMyRecord(var AMyRecord: TMyRecord); begin AMyRecord.Test_1 := ''; AMyRecord.Test_2 := ''; AMyRecord.Integer_1 := 0; AMyRecord.Integer_2 := 0; end; procedure SetTestValues(var AMyRecord: TMyRecord); begin AMyRecord.Test_1 := 'Hallo '; AMyRecord.Test_2 := 'Welt!'; AMyRecord.Integer_1 := 9; AMyRecord.Integer_2 := 2008; end; procedure TForm1.Button1Click(Sender: TObject); var MyRecord: TMyRecord; begin // undefinierte AnfangsWerte (beim ersten Test erscheinen hier Zufallswerte) ShowMessage(PrepareMsg(MyRecord)); // BeispielWerte zuweisen SetTestValues(MyRecord); ShowMessage(PrepareMsg(MyRecord)); // mit "händischer" Lösung auf definierte AnfangsWerte setzen ResetMyRecord(MyRecord); ShowMessage(PrepareMsg(MyRecord)); // BeispielWerte zuweisen SetTestValues(MyRecord); ShowMessage(PrepareMsg(MyRecord)); // "FillChar"-Lösung Finalize(MyRecord); FillChar(MyRecord, SizeOf(MyRecord), 0); ShowMessage(PrepareMsg(MyRecord)); end; Zum Anderen hat die obige "händische" Lösung zwar den Vorteil, dass man selbst definierte Werte erhält, aber den Nachteil, dass das bei umfangreicheren Records schnell recht aufwendig wird. Hinzu kommt, dass in meinem konkretten Fall (Verknüpfungen auslesen) mein Record auch noch einen weiteren Record enthält. Da ist dann die Frage, wie weit die händische Lösung sinnvoll ist.
Delphi-Quellcode:
type
TMyRecord = record Test_1 : string; Test_2 : string; Integer_1 : Integer; Integer_2 : Integer; FindData: TWIN32FINDDATA; // <-- weiterer Record in meinem Record end; @toms Die "MyBlankRecord"-Lösung entspricht in meiner Auffassung der "händischen" Lösung, ist hier also nicht gewollt. Die "FillChar"-Lösung erscheint mir eher als eine Lösung in meinem Sinne. Gewünscht ist eine Lösung mit kurzem Code, die schon-vorhandene Delphi Routinen benutzt. Und das wäre hierbei der Fall. Und es schein sogar recht elegant zu sein. OK, nach ersten Tests (siehe Komplett-Code oben) scheint die "FillChar"-Möglichkeit tatsächlich die gewünschte "elegange" Lösung zu sein. Ich werde nun noch Test mit dem zusätzlichen Record in meinem Record (FindDate: TWIN32FINDDATA) durchführen. @Ronny Meines Wissens nach werden mit FillChar auch die Strings auf "0" gesetzt (komplett mit Nullen gefüllt, also sozusagen Null-terminiert). Das entspricht einem leeren String und ist das gewünschte Ziel. Mal sehen, was die anderen sagen. :-D Guido. |
Re: Record zurücksetzen (löschen)
Hi,
Ein "Beweis", dass FillChar nicht funktioniert:
Delphi-Quellcode:
Dürfte dann übrigens ein schönes Speicherleck geben ;) (Außer der Memory-Manager räumt das trotzdem auf)
type
TBeweis = packed record EinString: String; Zahl: Integer; end; var Beweis: TBeweis; p: Pointer; begin Beweis.EinString:= 'Test'; p := @Beweis.EinString[1]; FillChar(Beweis,SizeOf(TBeweis),0); ShowMessage(PChar(p)); end; Wie SubData schon gesagt hat: Der Pointer zu dem eigentlichen String wird auf nil gesetzt. Aber der String selbst schwirrt noch im Speicher rum. Ist nur nichtmehr erreichbar, falls man den Pointer nicht vorher gesichert hat ;) |
Re: Record zurücksetzen (löschen)
Liste der Anhänge anzeigen (Anzahl: 1)
Du beweist, dass man jedes noch so simple Problemchen derart verunstalten kann, dass es eventuell nicht mal richtig funktioniert. :mrgreen:
Delphi-Quellcode:
Das ergibt das im Anhang.
MyRecord.nr := 1;
MyRecord.name := 'XYZ'; memo2.Lines.Add(IntToStr(MyRecord.nr)); memo2.Lines.Add('*'+MyRecord.name+'*'); memo2.Lines.Add(''); FillChar (MyRecord,SizeOf (MyRecord),0); memo2.Lines.Add(IntToStr(MyRecord.nr)); memo2.Lines.Add('*'+MyRecord.name+'*'); |
Re: Record zurücksetzen (löschen)
Jap, so in etwa dachte ich mir das.
Finalize scheint aber auch nicht zu funktionieren. Jedenfalls nicht, wenn das Record eine ThreadVar ist. Zumindest meldet FastMM danach noch Speicherlecks. Edit: Zitat:
Ob der String damit aber wirklich freigegeben wurde, wird daraus nicht deutlich. |
Re: Record zurücksetzen (löschen)
Edit: Kann weg...
|
Re: Record zurücksetzen (löschen)
Natürlich steht dann in MyRecord.Name nichts mehr. Weil der String "genilt" wurde. Aber der String selbst liegt immernoch im Speicher rum.
|
Re: Record zurücksetzen (löschen)
Und ich wette FastMM würde rumnörgeln ;-)
Edit: Michael: Magste das mal mit FastMM testen? |
Re: Record zurücksetzen (löschen)
Zitat:
|
Re: Record zurücksetzen (löschen)
Zitat:
|
Re: Record zurücksetzen (löschen)
FastMM meldet jedenfalls nichts.
|
Re: Record zurücksetzen (löschen)
Also es sieht laut FastMM4 so aus:
Delphi-Quellcode:
erzeugt KEIN MemoryLeak. Der MemoryManager schafft es wohl trotzdem den String nach der Methode mit dem Record freizugeben.
type
TBeweis = packed record EinString: String; Zahl: Integer; end; procedure TForm1.Button1Click(Sender: TObject); var Beweis: TBeweis; p: Pointer; begin Beweis.EinString:= 'Test'; p := @Beweis.EinString[1]; FillChar(Beweis,SizeOf(TBeweis),0); ShowMessage(PChar(p)); end; Anders siehts bei globalen Variablen oder Klassenvariablen aus. Da hinterlässt dieser Code ein MemoryLeak. Aber auch das kann verhindert werden:
Delphi-Quellcode:
Diese Zeile vor dem FillChar lässt das MemoryLeak verschwinden.
Finalize(Beweis);
Delphi-Quellcode:
@Hansa:// Globale- oder Klassenvariable var Beweis: TBeweis; procedure TForm1.Button1Click(Sender: TObject); var p: Pointer; begin Beweis.EinString:= 'Test'; p := @Beweis.EinString[1]; Finalize(Beweis); // <--- FillChar(Beweis,SizeOf(TBeweis),0); ShowMessage(PChar(p)); end; Ich weiß, dass es sich bei Records nicht um Pointer handelt. Aber das hat nichts damit zu tun. Fakt ist, dass mit FillChar nur der Pointer zu dem eigentlichen String überschrieben wird. Der String selbst liegt weiterhin im Speicher! Daran ist nichts zu rütteln. |
Re: Record zurücksetzen (löschen)
Zitat:
Delphi-Quellcode:
Man sollte sich das Leben einfach machen.
procedure MyRecordClear;
begin with MyRecord do begin Test_1 := ''; Test_2 := ''; Test_3 := ''; Integer_1 := 0; Integer_2 := 0; end; end; |
Re: Record zurücksetzen (löschen)
Damit keine Missverständnisse auftreten:
Der (die) String(s) sollen KEINESFALLS freigegeben werden! Im Gegenteil: Sie werden ja in meinem Programm benötigt! Sie sollen lediglich auf leer (, Integer auf 0, ...) gesetzt werden! (Speicherplatz soll beibehalten weren.) Der "Beweis" von Neutral General beweist nur, dass der Bereich im Speicher noch die Werte beinhaltet. Das ist ja auch in Ordnung. Ich gehe davon aus, dass FillChar nur die Länge(nangabe) des Strings auf 0 setzt. Das entspräche einem leeren String = gewünschtes Ergebnis. Speicherleck kann ich mir nicht vorstellen, da der Platz ja reserviert bleiben soll, bis die Record-Variable am Programm-Ende freigegeben wird. Guido. |
Re: Record zurücksetzen (löschen)
@Michael: Kannst du das mal mit einer globalen ThreadVar Variable testen?
Einmal mit Finalize und einmal ohne. Edit: @Guido: Das siehst du leider ein wenig falsch. Damit wird der Pointer auf den eigentlichen String überschrieben. Das bedeutet aber nicht, dass bei einer neuen Zuweisung der String wieder an der selben Stelle im Speicher liegt. Dementsprechend wird auch nicht die Längenangabe vom String auf 0 gesetzt, sondern der Pointer zum String. |
Re: Record zurücksetzen (löschen)
Zitat:
Guido. |
Re: Record zurücksetzen (löschen)
Zitat:
@Guido Eisenbeis: FillChar bewirkt schon, dass die Strings leer sind (leer darum, weil der Pointer zum String jetzt irgendwohin zeigt, wo kein String liegen wird). Aber wenn du das machst, musst/solltest du vorher Finalize(Record) aufrufen. Ansonsten gibt es wie gesagt ein MemoryLeak bei globalen (Thread) Variablen und Klassenvariablen. |
Re: Record zurücksetzen (löschen)
Danke!
Ich hab nämlich mal die böse Erfahrung gemacht, dass ich ThreadVar Records am Ende des Threads wieder explizit löschen muss, da sonst die Strings im Record MemoryLeaks verursachen. Den Fehler muss man erstmal finden :roll: |
Re: Record zurücksetzen (löschen)
Zitat:
|
Re: Record zurücksetzen (löschen)
@SubData: Die Hilfe hilft:
Zitat:
|
Re: Record zurücksetzen (löschen)
Weiß ich, hab ich danach auch gelesen.
|
Re: Record zurücksetzen (löschen)
Zitat:
Delphi-Quellcode:
Ist die ultimative Lösung :mrgreen: Damit werden auch Subrecords aufgeräumt. Habs ausprobiert.
Finalize(Record);
FillChar(Record,SizeOf(TRecord),0); |
Re: Record zurücksetzen (löschen)
Zitat:
@Michael + Ronny Danke für die Infos! Also seid ihr der Meinung, dass mit Finalize + FillChar sauber gearbeitet wird? Guido. |
Re: Record zurücksetzen (löschen)
Ja :)
Und bei der Gelegenheit: ![]() Hilft dabei Speicherlecks zu finden (Und optimiert auch gleich die Anwendung) |
Re: Record zurücksetzen (löschen)
Zitat:
Guido. |
Re: Record zurücksetzen (löschen)
Hi,
In den neueren Delphiversionen kann mans noch in eine Methode packen. Ist dann nachher etwas bequemer:
Delphi-Quellcode:
Edit: Schade, dass es keine Vererbung bei Records gibt, sonst hätte man da was TObject-mäßiges bauen können :stupid:
type
TRecord = record // ... procedure Clear; end; procedure TRecord.Clear; begin Finalize(Self); FillChar(Self,SizeOf(Self),0); end; |
Re: Record zurücksetzen (löschen)
Zitat:
Wie gesagt, interessiert mich nur etwas. Kann auch falsch sein. |
Re: Record zurücksetzen (löschen)
Zitat:
|
Re: Record zurücksetzen (löschen)
Zitat:
Edit: Hab mal was ausprobiert:
Delphi-Quellcode:
Damit dürften wohl alle Zweifel und Unklarheiten aus der Welt geschafft sein :)
// EinString = 'Test';
procedure TRecord.Clear; begin ShowMessage(EinString); // 'Test' Finalize(Self); ShowMessage(EinString); // '' FillChar(Self,SizeOf(Self),0); end; Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:53 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