Delphiのヘルプを見ると、New()で確保したメモリはDispose()で、.Creatで
作ったオブジェクトは.Freeで解放することと書いてあります。また、Freeメソッドの
ヘルプには
Free メソッドを呼び出すと,オブジェクトを廃棄できます。
Free メソッドは,オブジェクトの参照が nil ではない場合,
自動的にデストラクタを呼び出します。実行時にインスタンス化されたオブジェクトは,
オーナーを持っていない場合,Free メソッドを呼び出して廃棄しなければなりません。
結果として,オブジェクトが適切に処理され,メモリが解放されます。
と書かれています。「Freeメソッドでオブジェクトが廃棄されるなら、TList.Itemsも
破棄される」でしょうか?これをちょっと確かめるため、次のコードを実行してみました。
このプログラムは、フォーム上にボタンが1個配置された単純なものです。
Items[ i ]をDisposeせずにいきなりList.Freeを実行しています。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var List: TList;
i : Integer;
pIn : PInteger;
begin
List := TList.Create;
i := 0;
while i < 65535 do
begin
New(pIn);
i := i + 1;
pIn^ := i;
List.Add(pIn);
end;
// (A)
List.Free;
end;
end.
このプログラムを実行してボタンを数回押した場合、メモリ使用量が
3692kB(ボタン押す前)→4472kB(1回)→5240kB(2回)→6012kB(3回)と増えて
いきます。要するにNew()で確保してItemsプロパティに代入した場合でも、
FreeメソッドはItemsプロパティを解放しないということです。
では、New()手続きで確保したメモリをDispose()手続きで廃棄するよう、次の
コードを上の(A)部に挿入します。
i := 0;
while i < List.Count do
begin
if Assigned(List[i]) then Dispose(List[i]); // Assigned()でList[i]が
i := i + 1; // nilでないことを確認
end;
先ほどと同様にこのプログラムを実行すると、メモリ使用量は
3696kB(ボタン押す前)→3708kB(1回)→3708kB(2回)→3708kB(3回)と
メモリが増え続ける現象が出なくなります。
さて、これで十分でしょうか?ポインタ変数pInもNew(pIn)手続きを
しているのでDispose(pIn)手続きが必要じゃないでしょうか?これを確かめるため、
次のプログラムを実行してみましょう。このプログラムは、
数値を配列として使いたい - - リストの場合で使用したプログラムに、
Dispose(pIn)を加えたものです。確実に廃棄できるようfinally節に入れています。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Label1: TLabel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var List: TList;
i,Answer : Integer;
pIn: PInteger;
begin
List := TList.Create;
i := 0;
try
while i < 65535 do
begin
try
New(pIn);
i := i + 1;
pIn^ := i;
List.Add(pIn);
except
on E:EOutOfMemory do
begin
ShowMessage('メモリ不足です');
exit;
end;
end;
end;
i := 0;
Answer := 0;
while i < 65535 do
begin
Answer := Answer + Integer(List.Items[i]^);
i := i + 1;
end;
Label1.Caption := IntToStr(Answer);
finally
begin
Dispose(pIn); // ←追加したコード
i := 0;
while i < List.Count do
begin
if Assigned(List.Items[i]) then Dispose(List.Items[i]);
i := i + 1;
end;
List.Free;
end;
end;
end;
end.
結果は”無効なポインタ操作”エラーが発生します。理由は次の通りです。
New(pIn)で確保されたメモリは、List[ i ]に代入されていきます。
そのためwhile文が終了した時点ではpIn = List[65534]となっています。
Dispose(pIn)を行うと、List[65534]が持つメモリ領域を開放してしまいます。
その後にDispose(List[ i ])でList[ i ]が全て破棄されますが、List[65534]は既に
破棄されているため無効なポインタ操作となってしまうのです。
ようするに、代入したポインタ変数を削除しては
いけないのです。(List[65534]はDispose(List[i])で解放されますし、pIn変数は
Button1Click手続きが終了すればプログラムが破棄します)
|