Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Freigabe von Records, Reference Counting?
Xion - Di 12.02.13 20:37
Titel: Freigabe von Records, Reference Counting?
Hi,
wenn ich ein Record anlege, wird es standardmäßig freigeben. Z.B:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| type TInt=record i: integer; end; procedure DoSomething; var R: TIntRec; begin R.i:=50; end; |
Wenn ich das Record als Rückgabewert verwende, dann wird es (vorerst) nicht freigegeben.
Delphi-Quelltext
1: 2: 3: 4:
| function DoSomething: TIntRec; begin Result.i:=50; end; |
Jetzt zur Frage:
Was passiert mit dem Record, wenn ich einen Pointer darauf z.B. in eine Liste einfüge:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| var List: TList;
procedure DoSomething; var R: TIntRec; begin R.i:=50; List.Add(@R); end; |
Wann wird es nun freigegeben? Hoffentlich nicht beim Verlassen der Prozedur, sonst ist der Pointer in der Liste ja ungültig. Lösche ich nun den Pointer mit List.Delete(0), dann sollte das Record möglichst freigegeben werden, da ja nun kein Pointer mehr darauf existiert. Ist dies der Fall? Wenn ja, führt Delphi ein Reference Counting um zu bemerken, wenn kein Pointer mehr auf das Record zeigt? Das wäre in diesem Fall dann ziemlich Speicheraufwändig, da ich den Counter für jeden Integer benötige (also 50% overhead).
Unterm Strich die Frage:
Wenn ich das Record wie oben in die Liste einfüge, muss ich mir dann irgendwelche Sorgen um den Speicher machen? Ich habe lediglich Themen dazu gefunden, bei denen ein Pointer darauf mit New() angelegt wurde, der dann natürlich mit Dispose wieder freigegeben werden musste.
Habe dazu leider keine Antwort im Netz gefunden. Meine Tests vermitteln den Eindruck, dass Delphi es genau so macht wie ich es haben will (wie eigentlich immer, genau invers zu C++ :P).
Danke für die Antworten
Moderiert von
Narses: Topic aus Sonstiges (Delphi) verschoben am Mi 13.02.2013 um 00:03
WasWeißDennIch - Di 12.02.13 20:46
Nach meiner Kenntnis wird der Record wie alle lokalen Nicht-Pointer-Variablen nach Verlassen der Routine ungültig. Deshalb sollte man das wohl eher so schreiben:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| var List: TList;
procedure DoSomething; var R: PIntRec; begin New(R); R^.i:=50; List.Add(R); end; |
[edit] Oder anders ausgedrückt: AFAIK landen lokale Variablen auf dem Stack und werden nach Verlassen der enthaltenden Routine ungültig. Das betrifft in meinem Beispiel auch R, d.h. der Pointer R ist flüchtig, nicht aber die dahinterstehenden Daten. Durch das Hinzufügen zur Liste hingegen wird er dann auf dem Heap abgelegt und ist somit verfügbar. Genaugenommen ist das dann aber nicht mehr der "Original-R", sondern eine Kopie davon. [/edit]
Xion - Di 12.02.13 21:30
WasWeißDennIch hat folgendes geschrieben : |
Oder anders ausgedrückt: AFAIK landen lokale Variablen auf dem Stack und werden nach Verlassen der enthaltenden Routine ungültig |
Ah, ok, alles klar. Und wenn ich das Record als Rückgabewert zurückgebe, dann bleibt es vorerst auf dem Stack, bis es dann später vom Stack fliegt, wenn die aufrufende Prozedur endet?
GuaAck - Di 12.02.13 22:25
... oder Du eine andere FUNCTION oder PROCEDURE aufrufst, die den Stack überschreibt, also alles mehr oder weniger Zufall. Also entweder per NEW oder in einer übergordneten Variable. Übrigens: Wenn im Compiler die entsprechenden Warnungen aktiviert sind, dann sollte die Warnung "unsicher Code @Operator" kommen, genau aus den angeführten Gründen.
Gruß
GuaAck
Xion - Mi 13.02.13 20:30
GuaAck hat folgendes geschrieben : |
Also entweder per NEW oder in einer übergordneten Variable. |
Das gilt, wenn ich einen Pointer zurück gebe, oder? Wenn ich das Record selbst zurück gebe, dann wird eine Kopie zurückgegeben. Genau wie bei Integer. Oder etwa nicht?
Also mein neues Wissen mal angewendet:
Delphi-Quelltext
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:
| type TRec=record i: integer; end;
function Generate: TRec; begin Result.i:=25; end;
procedure Insert; var R: TRec; iP: ^Integer; begin R:=Generate; new(iP); iP^:=R.i; List.Add(iP); end;
procedure Delete; var iP: ^Integer; begin iP:=List.First; List.Delete(0); Dispose(iP); end;
procedure Main; begin List:=TList.Create;
Insert; Delete;
List.Free; end; |
Ok so, oder?
PS:
Hab jetzt die enstprechende Compiler-Warnung gefunden und aktiviert. Bei obigem Code sagt er:
[Warnung] (Zeile 17) W1047 Unsicherer Code '^-Operator'
Die Warnung kommt auch, wenn ich direkt Schreibe:
iP^:=25;. Vorschläge? ;)
FastMM4 findet zumindest keine Speicherleaks, also das ist ok.
Edit2:
Ich habe mehrfach gelesen, dass die Warnung lediglich was damit zu tun hat, das man mit .Net ^ und @ usw. nicht mehr benutzen kann. "sicher" bezieht sich daher eher auf "deprecated". Ich frage mich, wie ich eine generische Datenstruktur dann überhaupt machen soll, ohne Pointer ;) Delphi 2005 kennt ja noch keine Generics. Für alles Klassen? Bäh!
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!