Heyho,
tut mir echt Leid, wenn der Titel des Threads nicht so wirklich aussagekräftig ist, aber ich weiß nicht genau wie ich mein Problem sehr präzise beschreiben soll -- ich nehme gerne Verbesserungen entgegen
Und zwar habe ich mir einen Record geschrieben, der auf zwei verschiedene Arten funktionieren soll. Erst mal aber der Code, um den es im folgenden geht:
Delphi-Quellcode:
unit ExArray;
interface
type
/// <summary></summary>
TExArray<T> =
record
private const
USE_FIELD_FARRAY = '
UseFArray';
private type
TDynamicArray =
array of T;
TGenericArray = TArray<T>;
PGenericArray = ^TGenericArray;
public type
TToStringFunc = reference
to function (Value: T):
String;
private
FData : TGenericArray;
FArray : PGenericArray;
// Dirty Workaround: I have to use a String here because Delphi does not initialize
// a Pointer/Boolean/Integer value. So I have to "abuse" a String to have a flag.
FUseFArray :
String;
function GetArrayPointer(): PGenericArray;
public
class operator Implicit(Value: Pointer): TExArray<T>;
function ToString():
String;
end;
implementation
function TExArray<T>.GetArrayPointer(): PGenericArray;
begin
if (FUseFArray = USE_FIELD_FARRAY)
then
Result := FArray
else
Result := @FData;
end;
class operator TExArray<T>.Implicit(Value: Pointer): TExArray<T>;
begin
Result.FArray := Value;
Result.FUseFArray := USE_FIELD_FARRAY;
end;
function TExArray<T>.ToString():
String;
var
PArray : PGenericArray;
begin
PArray := GetArrayPointer();
// Umwandeln der Array-Element in einen String...
// Hier als Pseudocode:
//
// Result := '';
// for Element in GetArrayPointer()^ do
// Result := Result + ',' + Converter<T>.ToString(Element);
end;
end.
Es soll nun eben die folgenden beiden Möglichkeiten geben, den Code zu verwenden:
Delphi-Quellcode:
// 1. TExArray stellt eine Art Wrapper dar, der Array-Methoden zur Verfügung
// stellt und auf einem existierenden Array ausführt.
var
A : TArray<string>;
begin
SetLength(A, 10);
// Initialisierung des Arrays A...
Writeln(TExArray<string>(@A).ToString()); // Wichtig: das @ beachten!
end;
// 2. TExArray ist ein Record, der intern ein Array hält und Methoden bereit stellt,
// um dieses Array zu bearbeiten. Es muss somit kein explizites "externes" Array
// angelegt werden.
var
A : TExArray<string>;
begin
A.Init(['Foo', 'Bar', 'Test']);
A.Sort();
Writeln(A.ToString();)
end;
Ja, ich weiß, dass in der TExArray-Definition oben, keine Init und Sort Methoden angegeben sind, da dies nur eine gekürzte Fassung darstellt, die für das Problem relevant ist. Um nun diese beiden Versionen in einem Record zu beachten, habe ich einen Schalter eingeführt, die private Variable
FUseFArray. Es ist ja leider so, dass ein Record keinerlei primitiven Variablen à la Boolean oder Integer initialisiert, einen String jedoch schon! Daher dieser "dirty Workaround"... Wird nun also Version 1 genutzt, so wird der Implicit-Opertator aufgerufen und FUseArray entsprechend gesetzt. Wird Version 2 genutzt ist FUseArray eben ein Leerstring, da dieser ja initialisiert wird, was bei einem String immer der Leerstring ist. Entsprechend wirkt sich das auf GetArrayPointer(), eine Getter-Methode für die Array-Daten -- es wird niemals direkt FData oder FArray zugegriffen, sondern immer über GetArrayPointer().
Ich weiß, ist ziemlich viel Vorgeplänkel bisher, aber nun komme ich zu meinem eigentlichen Problem: der obige Code von TExArray führt zu einem Memory Leak, unabhängig vom generisch Typ T. Und zwar hängt dies mit dem internen String zusammen, wobei ich sagen muss, dass ich keine Ahnung habe, warum dies der Fall ist. Interessanterweise hängt es aber auch noch davon ab, wie man das ganze aufruft:
Delphi-Quellcode:
var
A : TArray<Integer>;
EA : TExArray<Integer>;
S : string;
begin
// Array-Initialisierung
SetLength(A, 2);
A[0] := 2;//'Foo';
A[1] := 1;//'BAr';
// Dieser Code erzeugt kein Memory Leak
EA := TExArray<Integer>(@A);
Writeln(EA.ToString());
// Der hier aber schon
Writeln(TExArray<Integer>(@A).ToString());
// Und dieser hier auch
S := TExArray<Integer>(@A).ToString();
Writeln(S);
end.
Ich habe das Projekt mal angehängt, sodass ihr es direkt testen könnt, wenn ihr wollt -- FastMM sollte halt vorhanden sein. Ebenso findet ihr die Ausgabe von FastMM bzgl. dem Leak. Wäre echt froh um jeden Vorschlag, den ihr dies bzgl. habt, da ich keinerlei Ahnung habe, wieso das es hier Probleme gibt...
»Remember, the future maintainer is the person you should be writing code for, not the compiler.« (Nick Hodges)