今配列について考えているのですが、ある配列をこのフォームのメンバー
として使いたいと思っています。教えてくれませんか?
たんに private か protected か public に宣言するだけです。
ある配列をこのフォームのメンバーとして使いたいと思っています、ではなく
どこかのオブジェクトのメンバーにして他から参照したいと思っております。
どこのオブジェクトがいいの
別の質問者さんですか?
オブジェクトはクラスのインスタンスです。
まず、クラスの public フィールドに配列を宣言しなければなりません。
> どこのオブジェクトがいいの
それは誰にも答えられません。あなたの用途が書いてないからです。
すいません、表記不足でした。少し長くなりますが
------------------------------
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
Button1: TButton;
ListBox1: TListBox;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
・・・
var
Form1: TForm1;
implementation
uses Unit2;
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Items.Add(Edit1.Text);
end;
procedure TForm1.Button2Click(Sender: TObject);
var
editString: String;
begin
editString := Edit1.Text;
Form2.Setedit1(editString);
Form2 := TForm2.Create(nil);
Form2.ShowModal;
end;
end.
--------------------------------
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
AAARecord = record
Edit2: String;
end;
TForm2 = class(TForm)
Edit1: TEdit;
Edit2: TEdit;
Button1: TButton;
procedure FormShow(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
public
procedure Setedit1(edit1: String);
end;
var
Form2: TForm2;
implementation
uses Unit1;
var
str: String;
aaaArr: array of AAARecord;
{$R *.DFM}
procedure TForm2.Setedit1(edit1: String);
begin
Str := Edit1;
end;
procedure TForm2.FormShow(Sender: TObject);
var i: Integer;
begin
Edit1.Text := str;
for i := Low(aaaArr) to High(aaaArr) do
begin
if Edit1.Text = Form1.ListBox1.Items[Form1.ListBox1.ItemIndex] then
begin
Edit2.Text := aaaArr[i].Edit2;
end;
end;
end;
procedure TForm2.Button1Click(Sender: TObject);
var i : Integer;
begin
SetLength(aaaArr, Form1.ListBox1.Items.Count);
for i := Low(aaaArr) to High(aaaArr) do
begin
if Edit1.Text = Form1.ListBox1.Items[Form1.ListBox1.ItemIndex] then
begin
aaaArr[i].Edit2 := Edit2.Text;
end;
end;
Self.Close;
end;
end.
というような感じです。
フォーム2で定義されているレコード型配列(ユニット変数に定義されている動的配列)をどこかのオブジェクトのメンバーにして使用したい場合、どこに記述すれば
よいでしょうか?
配列が必要な処理にみえませんが...
ソースコードを載せただけでどのような処理を行いたいのか説明が無いのもどうかと思います。
ソースコードからは該当オブジェクト無しとしか答えることができません。
配列が必要なんですよ。
簡単に要点だけを記述しただけです。
> どこかのオブジェクトのメンバーにして使用したい場合、どこに記述
どこかのオブジェクトにプロパティを作って
プロパティのアクセス関数で、Form2の配列を参照すれば良いのでは。
継承は?
回答ではありませんが。。。
> type
> AAARecord = record
> Edit2: String;
> end;
レコード型のメンバにString型があるとき、そのString型のメモリの開放がうまくいかないんじゃなかったかなと。
SetLengthで、要素数が減る場合、減る分の要素に対して Edit2 := '' とかして、文字列への参照を無くしておかないといかんかった気がする。
この辺は、過去ログあたりにもあるのかもしれないが、良ければ識者の方、教えてください。
[その他つっこみ]
> procedure TForm1.Button2Click(Sender: TObject);
> var
> editString: String;
> begin
> editString := Edit1.Text;
> Form2.Setedit1(editString);
> Form2 := TForm2.Create(nil);
> Form2.ShowModal;
> end;
Form2のインスタンスがクリエイトされる前に、Form2のメソッドSetedit1を使っている。
普通はエラーになるが、Form2の自動生成がOnになっており、かつ、
> var
> Form2: TForm2;
>
> implementation
このように、自動で作成されるグローバルな変数 Form2が存在するため、エラーにならない。
しかも、Form2のクリエイトのパラメータがnilのため、自動破棄されない。
いわゆるメモリーリークになる。
Form1のButton2をたたけばたたくほど、見えないForm2のインスタンスが次々作成されていく。
タスクマネージャーを起動し、Form1のButton2を連打しまくれば、使用メモリが次々増えていく様子を確認することができるでしょう。
ただし、Form2.OnCloseでAction = caFree としているなら、メモリーリークは発生しない。
発生しないが、
> Form2.Setedit1(editString);
ここで、破棄されたForm2のインスタンスにアクセスすることになり、結局エラーになるわけだ。
一般には、以下のように書くのが望ましいでしょう。
必要に応じて例外処理を記述すること。
procedure TForm1.Button2Click(Sender: TObject);
var
editString: String;
xxForm2: TForm2;
begin
editString := Edit1.Text;
xxForm2:= TForm2.Create(Self{nil});
xxForm2.Setedit1(editString);
xxForm2.ShowModal;
xxForm2.Free;
//または
//xxForm2.Releace;
end;
次
> procedure TForm2.Setedit1(edit1: String);
> begin
> Str := Edit1;
> end;
コンポーネントにも Edit1 があり、まぎらわしい。
今回は、引数の型がStringであるため大事に至らないが、バグの元である。
さらに次
> procedure TForm2.FormShow(Sender: TObject);
> var i: Integer;
> begin
> Edit1.Text := str;
> for i := Low(aaaArr) to High(aaaArr) do
> begin
> if Edit1.Text = Form1.ListBox1.Items[Form1.ListBox1.ItemIndex] then
> begin
> Edit2.Text := aaaArr[i].Edit2;
> end;
> end;
> end;
配列aaaArrのサイズとForm1.ListBox1のアイテム数が一致している保証がない。
最初のうちは、配列aaaArrのサイズが0のため、「Indexが範囲を超えています」というメッセージに遭遇していないだけです。
結論
やりたいことがわからない。
Unit2のユニット変数にしないで、TFormの変数にしてしまったほうが楽。
また、前述の通り、Record型のメンバにString型がある場合、メモリの行方が気になる。
今回、String型一つだけなので、むしろTStringListを使った方が吉。
勉強中だから、どうしても使いたいというのであれば、以下のようにするとイイかもな。
-----------------------------------------------
type
TArrayAAARecord = array of AAARecord;
funstion GetUnit2ArrayAAARecord: TArrayAAARecord;
implementation
> var
> str: String;
> //aaaArr: array of AAARecord;
aaaArr: TArrayAAARecord;
funstion GetUnit2ArrayAAARecord: TArrayAAARecord;
begin
Result := aaaArr;
end;
-----------------------------------------------
これで、GetUnit2ArrayAAARecordを使って配列を拾っておけば、メンバとして使えるでしょう。
ただし、array of HogeHoge のような動的配列は、暗黙ポインタ型。
例えば、以下のようにした場合、AとBは同じ内容(ポインタ)をさす。
したがって、実行結果は A[0] = 2 = B[0]
var
A, B: array of Integer;
begin
SetLength(A, 1);
A[0] := 1;
B := A;
B[0] := 2;
end;
じゃ、こっちはどうなる?
var
A, B: array of Integer;
begin
SetLength(A, 1);
B := A;
SetLength(A, 2);
//これって B = A なの?
end;
やったことがないので知りません。
実行結果が常に B = A なるのであれば、上記のTArrayAAARecord型宣言の方式で十分要求を満たすでしょう。
あとは、自分でやるべし。
> レコード型のメンバにString型があるとき、そのString型のメモリの開放がうまくいかないんじゃなかったかなと。
心配ありません。このレコード型の動的配列でも大丈夫。New Dispose でもOK。
レコード型の動的配列のメモリリークを防ぐ方法は?
ツイート | ![]() |