TStringsとTStringListの扱いについて

解決


かなもの  2005-09-08 00:45:00  No: 17458

お世話になっております かなものと申します

以前はTStringsとTStringListの初歩中の初歩の質問をさせて頂きました。
あれからいくつ調べたのですが、どうも自分の頭では理解しきれず苦戦しております。
今回、どうしてもわからない所がありましたので、
これも初歩中の初歩かと思いますが…質問させて頂きたいと思います。

やりたい事は、プリンターの一覧を取得して、それをComboBoxに入れて表示したいと考えています。

そこでまず、このように組んでみました。

procedure TForm1.FormCreate(Sender: TObject);
begin
        ComboBox1.Items := Printers.Printer.Printers;
end;

コレでしたら問題なく表示しました。ですが、これでは応用がききません。
そこで、一度リストに入れようと思い、色々調べた結果、このようにしてみました。

procedure TForm1.FormCreate(Sender: TObject);
var
        SetPrinters: TStrings;
begin
        SetPrinters := TStringList.Create;
        SetPrinters := Printers.Printer.Printers;
        ComboBox1.Items := SetPrinters;
        SetPrinters.Free;
end;

最初 SetPrinters を TStringList で宣言したら、型が違うと言われました。
TStrings に変えた所、今度はエラーが出なくなったので、これで良いのかな?と思っていたら、
これだと作成したアプリケーションの終了時にエラーが発生してしまいました。

そこで、下記のようにしたらエラーも出ず終了するようになりました。

procedure TForm1.FormCreate(Sender: TObject);
var
        SetPrinters: TStrings;
begin
        SetPrinters := Printers.Printer.Printers;
        ComboBox1.Items := SetPrinters;
end;

現在エラーが出ない上記の状態なのですが、これで良いのでしょうか?
それとも別の正しいやり方があるのでしょうか?

前回の質問の答えや、多くのページで(全てではないにしろ)TStringsは使うな的な事
が書かれていたのですが、これで本当に良いのか、自分ではわかりませんでした。

OS:WindowsXP SP2
Delphi6 Personal UP2


オヨヨ  2005-09-08 01:50:59  No: 17459

TStrings をつかうなって?
TStingList は  TStrings から派生してるから
TStringList もつかえないと思うけど・・・・

TStringList に中身があって
TStrings    には中身がないってこと


オヨヨ  2005-09-08 02:00:09  No: 17460

あ、それから回答ですが、

>現在エラーが出ない上記の状態なのですが、これで良いのでしょうか?
それでもよい!
が、しか〜〜し!
もともと存在していた ComboBox1.Items の
メモリ空間の処理すればの話だけど

俺なら Assign を使ってコピーする・・・


Syake  2005-09-08 02:07:27  No: 17461

ちなみに
>最初 SetPrinters を TStringList で宣言したら、型が違うと言われました
SetPrinters.AddStrings(Printers.Printer.Printers);
としました?
//--------------------------------------
var
   SetPrinters: TStringList;
begin
   SetPrinters := TStringList.Create;
   try
      SetPrinters.AddStrings(Printers.Printer.Printers);
      ComboBox1.Items := SetPrinters;
   finally
      SetPrinters.Free;
   end;
end;

>これだと作成したアプリケーションの終了時にエラーが発生してしまいました。
それは、宣言が違いますよね。


anone  2005-09-08 03:13:52  No: 17462

要するに抽象クラスと具象クラス、継承と多態性、上位クラスには下位クラスは
代入可能だがその逆は不可、という OOP の基本が分かってないのです。本を
一冊買えばいいのですが、Delphi 用の適当な候補を知りません。


にしの  2005-09-08 04:07:06  No: 17463

オヨヨさん
> 俺なら Assign を使ってコピーする・・・
ComboBox1.Items := SetPrinters;
は、
ComboBox1.Items.Assign(SetPrinters);
と同等ですので、代入でOKです。


kaiseki  2005-09-08 05:42:53  No: 17464

かなものさんの2番目のリスト
---
1: procedure TForm1.FormCreate(Sender: TObject);
2: var
3:        SetPrinters: TStrings;
4: begin
5:        SetPrinters := TStringList.Create;
6:        SetPrinters := Printers.Printer.Printers;
7:        ComboBox1.Items := SetPrinters;
8:        SetPrinters.Free;
9: end;
---
6:でPrintersのTStringsを代入すると 5:で代入したのが消えてしまう
8:でのFreeは 5:で作成したもののFreeではなくPrinters.Print.PrintersのFreeとなる(よって終了時にエラーとなる)
7:は一見代入に見えるが実際はAssign(SetPrinters)と同じこと(問題はない)
このやり方にするなら 6:は SetPrinters.Assign(Printers.Printer.Printers)とすべき
---
3番目のリスト
>procedure TForm1.FormCreate(Sender: TObject);
>var
>       SetPrinters: TStrings;
>begin
>        SetPrinters := Printers.Printer.Printers;
>        ComboBox1.Items := SetPrinters;
>end;
は問題ない
ComboBox1.Items := Printers.Printer.Printers;  と同じこと


かなもの  2005-09-08 07:11:14  No: 17465

皆様丁重な書き込みありがとうございました。
kaisekiさんの書き込みで、エラーの原因らしきものが見える事が出来ました。
3番目の書き方で問題ないという事なので、ひとまず解決と致します。

しかし、オロロさんの仰ってた
> TStringList に中身があって
> TStrings    には中身がないってこと
というのが、いまいち良くわかっていません。

kaisekiさんの書き込みの中の、
> 6:でPrintersのTStringsを代入すると 5:で代入したのが消えてしまう
> 8:でのFreeは 5:で作成したもののFreeではなくPrinters.Print.PrintersのFreeとなる(よって終了時にエラーとなる)
と話がつながる様に見えるのですが、

これは簡単に言えば、
SetPrinters := TStringList.Create;  は、空箱が出来上がり、
SetPrinters := Printers.Printer.Printers;  は、すでに値が入ってる箱が出来上がる。

みたいなイメージで良いのでしょうか?


kaiseki  2005-09-08 08:52:10  No: 17466

>SetPrinters := TStringList.Create;  は、空箱が出来上がり、
>SetPrinters := Printers.Printer.Printers;  は、すでに値が入ってる箱が出来上がる。
ちょっと違いますね。
SetPrinters := TStringList.Create;  は、新規に作成したTStringListの実体の場所(アドレス)をSetPrintersに記憶しておく。
SetPrinters := Printers.Printer.Printers;  は、すでに存在するTStringsの実体の場所をSetPrintersに記憶しておく。
です。違いがわかるでしょうか?

> 6:でPrintersのTStringsを代入すると 5:で代入したのが消えてしまう
というのは5:で作成したTStringListのアドレスを記憶しているのに6:で新たにアドレスを上書きしてしまうので5:でのアドレスが失われてしまうということで
5:で作成したTStringListの実体が消えてしまうことではありません。メモリに残ったままになっています。

> 8:でのFreeは 5:で作成したもののFreeではなくPrinters.Print.PrintersのFreeとなる(よって終了時にエラーとなる)
これは6:でSetPrinters := Printers.Printer.Printers;とコピー(Assign)ではなく代入しているので 8:の SetPrinters.Freeは Printers.Printer.Printersを Freeすることになり終了時に TPrinterが実体のなくなっている Printersを処理しようとしてエラーとなるものです。
この場合6:でSetPrinters.Assign(Printers.Printer.Printers); とコピーをするのなら
8:でのFreeは 5:で作成した TStringListをFreeすることになり問題はありません。

>しかし、オロロさんの仰ってた
>> TStringList に中身があって
>> TStrings    には中身がないってこと
はTStringsはanoneさんのおっしゃている「抽象クラス」ですよという意味です。
「抽象クラス」は検索して調べてみてください。
例えば http://www.borland.co.jp/tips/delphi/dh014/


kaiseki  2005-09-08 09:06:24  No: 17467

連続すいません。もうひとつ。
>3番目の書き方で問題ないという事なので、ひとまず解決と致します。
3番目の書き方の場合 SetPrinters は Printers.Printer.Printersを指していますので
参照とかする分には大丈夫ですが、SetPrintersを書き換える(変更する)場合はPrinters.Printer.Printersを変更することになりますから注意が必要です。


かなもの  2005-09-08 12:00:46  No: 17468

kaisekiさん、詳しい解説ありがとうございます。長文ですいません。

> SetPrinters := TStringList.Create;  は、新規に作成したTStringListの実体の場所(アドレス)をSetPrintersに記憶しておく。
> SetPrinters := Printers.Printer.Printers;  は、すでに存在するTStringsの実体の場所をSetPrintersに記憶しておく。
> 5:で作成したTStringListのアドレスを記憶しているのに6:で新たにアドレスを上書きしてしまうので5:でのアドレスが失われてしまうということで
> 5:で作成したTStringListの実体が消えてしまうことではありません。メモリに残ったままになっています。
SetPrinters := ???? は、代入しているように見えるが、実際は実体の場所を表してるという事ですが、
今回、自分はTStringListにコピーしたかったので、このままでは意図しない動きになりそうです。

上記の内容を見ると、
SetPrinters := TStringList.Create;
の部分が残ったままという事ですが、これは「アドレスが失われる=場所を指定する記録が無くなる」
となって放置状態になり開放も出来ず、メモリリークになるという解釈で良いのでしょうか?

又、最初の箱のイメージで例えると
SetPrinters := TStringList.Create;  は、空箱を新たに作成して、その場所を指定する。
SetPrinters := Printers.Printer.Printers;  は、Printersという値が入っている箱の場所を知らしている。
みたいなイメージで良いのでしょうか?

> 6:でSetPrinters := Printers.Printer.Printers;とコピー(Assign)ではなく代入しているので 8:の SetPrinters.Freeは Printers.Printer.Printersを Freeすることになり終了時に TPrinterが実体のなくなっている Printersを処理しようとしてエラーとなるものです。
> この場合6:でSetPrinters.Assign(Printers.Printer.Printers); とコピーをするのなら
> 8:でのFreeは 5:で作成した TStringListをFreeすることになり問題はありません。
という事は、このままでは
SetPrinters := Printers.Printer.Printers;
SetPrinters.Free;

Printers.Printer.Printers.Free;
は同じ意味で、
この時点でアプリ起動時に読み込んだTPrinterが無くなり、
アプリ終了時に開放済みの存在しないTPrinterを開放しようとして
エラーが出る…という事でしょうか?

> 「抽象クラス」は検索して調べてみてください。
> 例えば http://www.borland.co.jp/tips/delphi/dh014/
ありがとうございます。anoneさんが仰ってた
> 要するに抽象クラスと具象クラス、継承と多態性、上位クラスには下位クラスは
> 代入可能だがその逆は不可、という OOP の基本が分かってないのです。
という指摘ですが、まさに的中でして、勉強中ではありますが、思うように進まず苦戦をしております。
ボーランドのtipsページはいくつか見た事がありますが、このページは初めて見ます。
ページを見て、イロイロ勉強したいと思います。

> 3番目の書き方の場合 SetPrinters は Printers.Printer.Printersを指していますので
> 参照とかする分には大丈夫ですが、SetPrintersを書き換える(変更する)場合はPrinters.Printer.Printersを変更することになりますから注意が必要です。
となると、自分の目的は場所を指定するのではなく、一覧を「コピー」したいので、
SetPrinters.Assign(Printers.Printer.Printers);
の方を使うべきだと判断します。

今回の件を元に、下記のような内容に変更しました。(try文は除く)
SetPrinters := TStringList.Create;
SetPrinters.AddStrings(Printers.Printer.Printers);
ComboBox1.Items := SetPrinters;
SetPrinters.Free;

実際には、間に挟むものはありますが、これで安心して一覧を扱えそうです。


かなもの  2005-09-08 21:56:23  No: 17469

先ほど上記内容を実際に動かした所、問題なく動いております。
改めて解決印を付けたいと思います。有難う御座いました。

尚、最初に「TStringsは使うな的な事…」と紛らわしい書き方をしてしまいましたが、
これは TStrings.Create; を使うな…という意味です。すいませんでした。


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

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






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