こんな関数作る人はいないのか?

解決


  2007-03-22 12:25:37  No: 25433

素朴な疑問ですがどなたかお分かりになりましたら。
戻り値が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に関する認識が間違っているのでしょうか?下手な日本語ですが疑問のポイントがご理解いただけますか?


orz  2007-03-22 17:54:34  No: 25434

適当に改行希望

> 呼び出すごとに新たにインスタンスが作られリストが増え続けていくのですか?
Yes

> freeとしなくてはまずいですよね
Yes

> でもこの場合は何をfreeすればよいのでしょう?
???

var 
  xxx:TStringList;
begin
  xxx := test;
  //text関数の結果を受け取って Freeしておけばいい。
  xxx.Free;
end;

> 私のcreste,freeに関する認識が間違っているのでしょうか?
たぶん・・・Integerとかの普通の変数と、Create,Freeが必要なクラスの差がわかっていないのかもしれぬ。

> 再びこの関数を呼び出すとエラーになると思う
だからこの発言になるのではないかと。

ばんがれ


  2007-03-22 18:44:40  No: 25435

orzさんこんばんわ。
また伝わりにくい日本語になるかもしれませんが、よくわからないのはtestという関数の形をしたTstringListのインスタンスを明示的に作ったのですがこれは開放しなくてよいのかという事です。おっしゃるようにxxxという変数に移しかえて処理を行い最終的にfreeとすれば処理自体に問題はないと思いますが、xxx := test;とした時にtestというTstringlistのインスタンスが作成されているはずだと思うのですが、これはfreeしなくてもメモリリークにはならないのかということです。何か大きな認識違いを起こしていますか?よろしければご指摘下さい。


  2007-03-22 19:05:14  No: 25436

すみません改行忘れました(陳謝)


嗚呼カンチガイ  2007-03-22 19:50:32  No: 25437

>xxx := test;とした時にtestというTstringlistのインスタンスが作成されているはずだと思うのです...
それが勘違い。
xxxという変数に代入されるのは単なるポインタ(4byte)。
ローカル変数が少なければ、その格納場所はレジスタになるので、メモリは使われない。
ローカル変数が増えれば、その格納場所はスタックが使われる。その場合に使用されるメモリ領域は、関数(手続き)から出る時に自動的に解放される。

>こんな関数作る人はいないのか?
VCLでも使われていていると思うけど、そういう関数作っても、チャントそれを呼出した手続き(関数)の方でFreeしておけば無問題。


del5  2007-03-22 20:05:28  No: 25438

基本は全て同じ...
呼び出す側、呼び出される側で共通に変数を扱いたければ、
呼び出す側で変数を用意しておく必要あり。

// 呼び出される側
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;


ろうばしん?  2007-03-22 21:01:53  No: 25439

>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;


del5  2007-03-22 21:49:32  No: 25440

> ろうばしん? さん

失礼しました(却って混乱?)...  m(__)m
ばたばたで、頭で考えたのと質問者のリストとミックスしてしまいましたね。

それが、正解です。


  2007-03-22 22:14:09  No: 25441

わかりました!(と思います)
関数を抜けた時点で開放されているんですね。
その値を何か変数で受けておけば値は保持されるし
不要になったら変数を開放すればよいと...ですよね
ですから私が最初に出したサンプルコードはあれでよくて
メモリリークはないわけですね。そういえば関数内で宣言した
変数は関数抜けた時点でなくなってますもんね、その理屈ですよね。
ああすっきりした。以前からコーディングをした後で全体を見直して
ステップ数を減らすダイエットをやっていたんですが、このような
コードはリークを起こすのではとびくびくしていたんです。
いろいろありがとうございました。

...ところで

<関数側>
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;

は同じ結果になると思うのですが、いろんな意味でどちら
がベターでしょう?
皆さんはどちらを使われるんですか?


うんと  2007-03-22 22:41:56  No: 25442

> procedure test(var list: TStringList);

この場合は var はいりませんね。

わたしだったら下の方にします。コンストラクタとデストラクタ(この場合は Free だが) が同じ可視性になっているからです。


sk  2007-03-27 20:56:19  No: 25443

><関数側>
>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の戻り値がインスタンスへのポインタです。


  2007-03-29 09:48:25  No: 25444


うんと  2007-03-29 12:01:56  No: 25445

メモリリークより便利さを重視したいなら、それでもいいんじゃないですか。

わたしなら、インスタンスの作成と破棄は同じスコープでやりたいですけど。


うんと  2007-03-29 12:06:18  No: 25446

>えっ???ってことは前者ではどんどんリークしていくわけですか?

はい、コンストラクタと Free は同じ回数だけ実行しなくてなりません。
前者は、Free がないので、関数を呼ぶたびごとにリークします。
ちゃんと変数で受け取って、使った後に Free をするならいいのですけど。


orz  2007-03-29 17:20:56  No: 25447


sk  2007-03-29 20:14:46  No: 25448

少なくともメモリリークを起こしてまで簡便性にこだわるのもどうかとおもいます
StringListじゃなくてStringの動的配列を返すようにする方法もあります
これならFreeしなくていいはずです


こんなのアリ  2007-03-29 23:16:36  No: 25449

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


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加