QCにレポート済みですが、Delphi 2006 の Win32用 Delphi で
レコード型のコンストラクタを使うとメモリリークすることがあります。
QC にレポートした例です。
1) Create Unit as below
unit VectorU;
interface
uses
SysUtils;
type
TVectorElements = array of Extended;
TVector = record
private
Fe: TVectorElements;
public
constructor Create(Size: Integer);
end;
implementation
{ TVector }
constructor TVector.Create(Size: Integer);
begin
SetLength(Fe, Size);
end;
end.
2) Call TVector's Constructors as below
procedure Test;
var
a: TVector;
begin
a := TVector.Create(1000000);
// Compiler generate code to initialize variable a (Initialize(a)) before
// Excecuting the body of Constructor. This behavior
// throws away memory allocated for a.Fe
a := TVector.Create(1000000);
end;
Call Test causes 10MB memory leak!!
回避方法ですが、
a := TVector.Create(1000000);
Finalize(a);
a := TVector.Create(1000000);
というように、コンストラクトする対象をあらかじめ Finalizeしておけば
大丈夫です。
補足:
CPUウィンドウで追ってみると、
a := TVector.Create(1000000);
の行では、まず Initialize(a) が呼ばれて動的配列のフィールドがクリアされ、
それから a.Fe に対して SetLength(Fe, Size); が実行されます。
従って、元々 a.Fe に入っていた動的配列が破棄されずに新しいメモリが
割り当てられてしまいます。
Delphi では戻り値は参照渡しのパラメータとして扱われるので、
a := TVector.Create(1000000); は a.Create(1000000); と同等になるようです。
a.Create(1000000); と書き換えてもメモリリークは発生します。
構文の形式上、従来どおり、aが抱えているメモリ(文字列、インターフェース、動的配列等)を
Finalize で破棄してから内容を変更するか、あるいは、CopyRecord(Delphiの内部関数)でaを
変更するべきなので、コンパイラのバグと思われます。
レコード型のコンストラクタが導入されたことで、構造体の初期化/再初期化をコンストラクタを使って
やることが当たり前になると思いますが、まだこんな落とし穴があるのでご注意を!
-------------------------------------------------------------------
中村拓男 東京都日野市
中村の里 http://www.asahi-net.or.jp/~HA3T-NKMR/ 2002/8/15
DGSサポート http://www.asahi-net.or.jp/~HA3T-NKMR/DGS/ 2002/9/26
PGP FP: AF 31 35 65 65 DB C1 8A AF F8 EC 08 CE 49 FF BA