素朴な疑問ですがどなたかお分かりになりましたら。
戻り値がTStringListの関数を作ることはトラブルの元でしょうか?たとえば
function test: TStringList;
var
i : integer;
begin
Result := TStringList.Create; {インスタンス作成}
for i:=0 to 10 do begin {別に何でもいいんですけど}
Result.Add(IntToStr(i));
end;
end;
上の例で戻り値は0から10のリストとなりますが、私の考えだとResult.Free;をしていないので再びこの関数を呼び出すとエラーになると思うのですがところがどっこい通ります。関数の最後でResult.Free;としてしまうとリストが得られませんので意味がないのは当然ですが、それにしてもこれが通るということは呼び出すごとに新たにインスタンスが作られリストが増え続けていくのですか?いずれにせよプログラムを終了するときにはfreeとしなくてはまずいですよね。でもこの場合は何をfreeすればよいのでしょう?私のcreste,freeに関する認識が間違っているのでしょうか?下手な日本語ですが疑問のポイントがご理解いただけますか?
適当に改行希望
> 呼び出すごとに新たにインスタンスが作られリストが増え続けていくのですか?
Yes
> freeとしなくてはまずいですよね
Yes
> でもこの場合は何をfreeすればよいのでしょう?
???
var
xxx:TStringList;
begin
xxx := test;
//text関数の結果を受け取って Freeしておけばいい。
xxx.Free;
end;
> 私のcreste,freeに関する認識が間違っているのでしょうか?
たぶん・・・Integerとかの普通の変数と、Create,Freeが必要なクラスの差がわかっていないのかもしれぬ。
> 再びこの関数を呼び出すとエラーになると思う
だからこの発言になるのではないかと。
ばんがれ
orzさんこんばんわ。
また伝わりにくい日本語になるかもしれませんが、よくわからないのはtestという関数の形をしたTstringListのインスタンスを明示的に作ったのですがこれは開放しなくてよいのかという事です。おっしゃるようにxxxという変数に移しかえて処理を行い最終的にfreeとすれば処理自体に問題はないと思いますが、xxx := test;とした時にtestというTstringlistのインスタンスが作成されているはずだと思うのですが、これはfreeしなくてもメモリリークにはならないのかということです。何か大きな認識違いを起こしていますか?よろしければご指摘下さい。
すみません改行忘れました(陳謝)
>xxx := test;とした時にtestというTstringlistのインスタンスが作成されているはずだと思うのです...
それが勘違い。
xxxという変数に代入されるのは単なるポインタ(4byte)。
ローカル変数が少なければ、その格納場所はレジスタになるので、メモリは使われない。
ローカル変数が増えれば、その格納場所はスタックが使われる。その場合に使用されるメモリ領域は、関数(手続き)から出る時に自動的に解放される。
>こんな関数作る人はいないのか?
VCLでも使われていていると思うけど、そういう関数作っても、チャントそれを呼出した手続き(関数)の方でFreeしておけば無問題。
基本は全て同じ...
呼び出す側、呼び出される側で共通に変数を扱いたければ、
呼び出す側で変数を用意しておく必要あり。
// 呼び出される側
function abc:integer;
begin
result 100;
end;
function xyz:TStringList;
begin
result.add('xxx');
end;
// 呼び出す側
var
i:integer;
slist:TStringList;
begin
i:=abc;
slist:=TStringList.Create;
slist:=xyz;
slist.free;
end;
>function xyz:TStringList;
>begin
> result.add('xxx');
>end;
>
>// 呼び出す側
>var
> slist:TStringList;
>begin
> slist:=TStringList.Create;
> slist:=xyz;
> slist.free;
>end;
↑このサンプルはマズイでしょ。(Access Violation発生)
呼び出す側でCreateしておくなら、正しくは、
procedure xyz(L: TStringList);
begin
L.add('xxx');
end;
// 呼び出す側
var
slist: TStringList;
begin
slist:=TStringList.Create;
//slist:=xyz;
xyz(slist);
...
slist.free;
end;
> ろうばしん? さん
失礼しました(却って混乱?)... m(__)m
ばたばたで、頭で考えたのと質問者のリストとミックスしてしまいましたね。
それが、正解です。
わかりました!(と思います)
関数を抜けた時点で開放されているんですね。
その値を何か変数で受けておけば値は保持されるし
不要になったら変数を開放すればよいと...ですよね
ですから私が最初に出したサンプルコードはあれでよくて
メモリリークはないわけですね。そういえば関数内で宣言した
変数は関数抜けた時点でなくなってますもんね、その理屈ですよね。
ああすっきりした。以前からコーディングをした後で全体を見直して
ステップ数を減らすダイエットをやっていたんですが、このような
コードはリークを起こすのではとびくびくしていたんです。
いろいろありがとうございました。
...ところで
<関数側>
function test: TStringList;
begin
Result := TStringList.Create;
Result.Add('OK');
end;
<呼び出し側>
showmessage(test.text);
というのと
<手続き側>
procedure test(var list: TStringList);
begin
list.Add('OK');
end;
<呼び出し側>
var
lst : TStringList;
begin
lst := TStringList.Create;
test(lst);
showmessage(lst.text);
lst.Free;
end;
は同じ結果になると思うのですが、いろんな意味でどちら
がベターでしょう?
皆さんはどちらを使われるんですか?
> procedure test(var list: TStringList);
この場合は var はいりませんね。
わたしだったら下の方にします。コンストラクタとデストラクタ(この場合は Free だが) が同じ可視性になっているからです。
><関数側>
>function test: TStringList;
>begin
>Result := TStringList.Create;
>Result.Add('OK');
>end;
>
><呼び出し側>
>showmessage(test.text);
これじゃメモリリークしてます。
関数を抜けた時点では解放されていません。
みんな言ってますがFreeは必要です。
>>xxx := test;とした時にtestというTstringlistのインスタンスが作成されているはずだと思うのです...
>それが勘違い。
>xxxという変数に代入されるのは単なるポインタ(4byte)。
嗚呼カンチガイさんの言い方が分かりづらいので混乱してるようですが、
test呼んだ時点でTStringListのインスタンスは確保されています。
xxxに代入されるのは確かにTStringListのインスタンスを指すポインタです。
testは関数名であってインスタンスではありません。testの戻り値がインスタンスへのポインタです。
メモリリークより便利さを重視したいなら、それでもいいんじゃないですか。
わたしなら、インスタンスの作成と破棄は同じスコープでやりたいですけど。
>えっ???ってことは前者ではどんどんリークしていくわけですか?
はい、コンストラクタと Free は同じ回数だけ実行しなくてなりません。
前者は、Free がないので、関数を呼ぶたびごとにリークします。
ちゃんと変数で受け取って、使った後に Free をするならいいのですけど。
少なくともメモリリークを起こしてまで簡便性にこだわるのもどうかとおもいます
StringListじゃなくてStringの動的配列を返すようにする方法もあります
これならFreeしなくていいはずです
Button1を何回クリックしてもメモリリークなし?
use AutoPointer;
var
AutoObjRef: IAutoObjRef;
function test: TStringList;
begin
result := TStringList.Create;
AutoObjRef := TAutoObjRef.Create(result);
result.Add('xxx');
result.Add('yyy');
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(test.Text);
end;
AutoPointer.pasはココ↓
http://leed.issp.u-tokyo.ac.jp/~takeuchi/delphi/browse.cgi?index=41732
ツイート | ![]() |