喫茶XRAYトップページ

[スレッド全体
]

[9963] Re3:動的配列とポインタと型と...返信 削除
2016/5/19 (木) 21:48:06 Mr.XRAY
▼ takiさん

> > 『var』と書いていないのに var 扱いになる!!

正確には違いますが...
動作そのものは,taki さんが提示された通りですね.

手続きの中で動的配列の SetLength を初めて実行するということは,
クラスで言えば,手続きの中でそのクラスのインスタンスを生成
することと同じです.
こういう使い方はしたことがないです.私は.

> 私はこんな使い方しているもんで、それで気が付いたのでご報告を。

報告ということで... (^^;


[9965] インスタンスの生成をどこでやるか返信 削除
2016/5/19 (木) 22:44:01 Mr.XRAY
> こういう使い方はしたことがないです.私は.

基本的に以下のような使い方をしています.
インスタンスの生成は,呼び出し側で行う方法です.
この方法が,割と一般的だとは思います.
クラス等は,呼出し側で生成と破棄を実行した方が,管理がしやすくなります.


type
  TTestAry = array of integer;


procedure ABC(buf:TTestAry);
begin
  //呼出し側でbufのインスタンスを生成していなければ例外発生
  buf[0] := 999;
  Form1.Label1.Caption := inttostr(buf[0]);   // 999
  // 手続き内で値を変更する
  buf[0] := 888;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Abuf:TTestAry;
begin
  //インスタンス生成(と同じ)
  SetLength(Abuf,1);

  // 手続きを呼び出す
  ABC(Abuf);
  Label2.Caption := inttostr(Abuf[0]);

  //動的配列の解放はしなくてもOK
  //明示的に実行するのであればnilを代入する
  Abuf := nil;
end;



[9967] Re:インスタンスの生成をどこでやるか返信 削除
2016/5/20 (金) 19:00:25 taki
▼ Mr.XRAYさん
> > こういう使い方はしたことがないです.私は.
>
> 基本的に以下のような使い方をしています.
> インスタンスの生成は,呼び出し側で行う方法です.
> この方法が,割と一般的だとは思います.
> クラス等は,呼出し側で生成と破棄を実行した方が,管理がしやすくなります.
>


やはり、そうですよね。
ありがとうございました。


[9971] Re2:インスタンスの生成をどこでやるか返信 削除
2016/5/21 (土) 22:58:38 taki
う〜ん、手続き内で動的配列の数を変更すると Ver 指定する必要があるようです。
Ver が無いと100〜109しか表示されません。

type
  TTestAry = array of integer;

procedure ABC(var buf:TTestAry);
var
  i,len,count:integer;
begin
  len := Length(buf);  // 今のサイズを得る
  count := 0;

  // 100〜200のデータをbufにセットする処理
  for i := 100 to 200 do begin
     buf[count] := i;
     inc(count);

    // 配列の数を増やす ---------
    if count >= len then begin
      len := len + 10;    // とりあえず10増やす
      SetLength(buf,len);
    end;
    // --------------------------
  end;

  // 終了前に配列の数を実際の数に揃える
  SetLength(buf,count);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Abuf:TTestAry;
  i,len:integer;
begin
  // 動的配列設定
  SetLength(Abuf,10);   // とりあえず10確保

  // 手続きを呼び出す
  ABC(Abuf);

  // 結果の表示
  len := Length(Abuf);
  for i := 0 to len - 1 do begin
    ListBox1.Items.Append(inttostr(Abuf[i]));
  end;
end;


ListBox1.Items.Append()のように使えたらと思ったのですが、
どうもちがうようです。

やはり呼び出し側でSetLength(Abuf,10000)とかにし、ABCを関数にして
実際に処理した数を返すようにするのが正解かもしれません。


[9972] Re3:インスタンスの生成をどこでやるか返信 削除
2016/5/21 (土) 23:44:27 Mr.XRAY
▼ takiさん

> ListBox1.Items.Append()のように使えたらと思ったのですが、
> どうもちがうようです。


整数型を配列,または配列のように(?) に扱う場合,
どんな目的で使用するのかにもよると思いますが,
TListBox のイメージで言えば,

TLst<Integer> 等も視野に入れてもいいのかな ? とも思いますが,
どうでしょう.

[TList<T> による動的な整数配列]
http://mrxray.on.coocan.jp/Delphi/Others/TypeArray.htm#06

[System.Generics.Collections.TList]
http://docwiki.embarcadero.com/Libraries/XE8/ja/System.Generics.Collections.TList


# 既にテストで確認していますが,引数がポインタ型の時に,var を指定すると,
# 挙動の違いにより,トラブルが発生した時に面倒になるかも知れません.


[9973] Re3:インスタンスの生成をどこでやるか返信 削除
2016/5/22 (日) 01:30:27 Nov
▼ takiさん
> う〜ん、手続き内で動的配列の数を変更すると Ver 指定する必要があるようです。
> Ver が無いと100〜109しか表示されません。


SetLengthすると、bufのアドレスが変わりませんか?

つまり、varはポインタそのものに効果があるだけですよね(たぶん)。

[9974] Re4:インスタンスの生成をどこでやるか返信 削除
2016/5/22 (日) 01:48:11 Nov
> つまり、varはポインタそのものに効果があるだけですよね(たぶん)。

あ、これだけ読むとなんのことかよくわからないですね。
「ポインタにvarをつけた場合は」ってことです。

[9975] Re5:インスタンスの生成をどこでやるか返信 削除
2016/5/22 (日) 12:09:13 Mr.XRAY
▼ Novさん
> > つまり、varはポインタそのものに効果があるだけですよね(たぶん)。
>
> あ、これだけ読むとなんのことかよくわからないですね。
> 「ポインタにvarをつけた場合は」ってことです。


ですね.クラス,動的配列の引数は,元々クラス参照と同じとなります.
var を付けると,その参照となります.
動的配列は,Create しなくても使用できてしまうので,Class 型で
確認してみます.
(注: このコードはメモリリークが発生します)

type
  TTestClass = class(TObject);


procedure ABC(var AClass: TTestClass);
var
  P : PInteger;
begin
  AClass := TTestClass.Create;
  P := @AClass;
  Form1.Label2.Caption := IntToStr(P^);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  AClass : TTestClass;
  P      : PInteger;
begin
  AClass := nil;
  AClass := TTestClass.Create;
  try
    P := @AClass;
    Label1.Caption := IntToStr(P^);

    ABC(AClass);
    if AClass <> nil then begin
      P := @AClass;
      Label3.Caption := IntToStr(P^);
    end;
  finally
    FreeAndNil(AClass);
  end;
end;




[9976] Re6:インスタンスの生成をどこでやるか返信 削除
2016/5/22 (日) 12:35:17 Mr.XRAY
> ですね.クラス,動的配列の引数は,元々クラス参照となります.
> var を付けると,その参照となります.


あまり正確とは言えませんが,当サイトの記事には,左側に目次があります.
その目次をクリックすると,目次のタイトルに応じた本文が表示されます.
引数を「値」で渡す場合,この目次のタイトルそのものが渡されます.
その「値」の引数に var を付けると,目次のタイトルを,関数内で
変更できます.
var は元々変数の定義に使われ,それは値を変更できますから.

引数がクラス等のポインタの場合,目次の場所,アドレスが渡されます.
つまり,目次のタイトルそのものは関数内では変化しません.
目次のアドレス(記事のアドレスであるURL)が示す,記事の内容を取り出したり,
変更することができます.

もし,それに var を付けると,... ということになりますね.
ということで,参照渡しの引数に var を付けるのは,
一般的には使用することがないということになります.
私としては,var を使わない方向でコーディングを考えることをお勧めます.
と言うより,そうすべきと考えていますです.ハイ.


[9978] Re7:インスタンスの生成をどこでやるか返信 削除
2016/5/22 (日) 18:15:55 Mr.XRAY
> と言うより,そうすべきと考えていますです.ハイ.

いや,私は var を使いたい,というのであれば,やぶさかではございません.
人様のコーディングに口出しするような,そんな恐ろしいことはできません.ハイ.(^^;


[9980] Re8:インスタンスの生成をどこでやるか返信 削除
2016/5/22 (日) 19:28:54 taki
▼ マスター、Novさん

色々と説明して頂き恐縮です。

何度も読み返したのですが、私の頭では理解できません。
それで、以下のコードを試してみました。

それぞれのポイントでの変数の内容と変数のアドレスを
表示するようにしただけです。


type
  TTestAry = array of integer;

procedure ABC(var buf:TTestAry);
var
  i,len,count:integer;
  s:string;
begin
  s := 'ABC手続 buf ' + IntToHex(Integer(buf),8);
  Form1.listbox1.Items.Append(s);
  s := 'ABC手続 @buf ' + IntToHex(Integer(@buf),8);
  Form1.listbox1.Items.Append(s);

  len := Length(buf);  // 今のサイズを得る
  count := 0;

  // 100〜200のデータをbufにセットする処理
  for i := 100 to 200 do begin
     buf[count] := i;
     inc(count);

    // 配列の数を増やす ---------
    if count >= len then begin
      len := len + 10;    // とりあえず10増やす
      SetLength(buf,len);

      s := 'SetLength' + inttostr(len) + ' buf ' + IntToHex(Integer(buf),8);
      Form1.listbox1.Items.Append(s);
      s := 'SetLength' + inttostr(len) + ' @buf ' + IntToHex(Integer(@buf),8);
      Form1.listbox1.Items.Append(s);

    end;
    // --------------------------
  end;

  // 配列の数を実際の数に揃える
  SetLength(buf,count);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Abuf:TTestAry;
  i,len:integer;
  s:string;
begin
  // 動的配列設定
  SetLength(Abuf,10);   // とりあえず10確保

  s := 'Button1手続 SetLength Abuf ' + IntToHex(Integer(Abuf),8);
  Form1.listbox1.Items.Append(s);
  s := 'Button1手続 SetLength @Abuf ' + IntToHex(Integer(@Abuf),8);
  Form1.listbox1.Items.Append(s);

  // 手続きを呼び出す
  ABC(Abuf);

  s := '戻った Abuf ' + IntToHex(Integer(Abuf),8);
  Form1.listbox1.Items.Append(s);
  s := '戻った @Abuf ' + IntToHex(Integer(@Abuf),8);
  Form1.listbox1.Items.Append(s);

  // 結果の表示
  len := Length(Abuf);
  for i := 0 to len - 1 do begin
    ListBox1.Items.Append(inttostr(Abuf[i]));
  end;
end;


その結果、@Abuf、@bufともに常に同じアドレスを示していました。多分、
procedure TForm1.Button1Click(Sender: TObject); で定義した
var
  Abuf:TTestAry; ← のアドレスだと思います。

Abuf、bufは時々変化していました。
Abuf、bufは動的配列の先頭位置のアドレスを示している筈です。
Novさんが言われている通りSetLengthすると時々アドレスが変わっています。
多分、確保したメモリー空間で収まらなくなったので、別の広いメモリー空間に
移動した?のではないかと想像しています。

いや、そういう問題じゃなくてトラブルのもとになるようなコードを
書くべきじゃないとマスターは言われているのだと思いますが、とりあえず
この結果でこの件は自分としては納得しました。

今作成中のプログラムも「varなし」の方法に書き換えていきます。

こんな事にマスターを巻き込んでしまいスミマセンでした。


[9981] Re9:インスタンスの生成をどこでやるか返信 削除
2016/5/22 (日) 20:47:59 Mr.XRAY
> いや、そういう問題じゃなくてトラブルのもとになるようなコードを

ですね.割とそういち立場に立っています.ホホホッ.
最近追加した記事に,管理者権限のことを書いています.
では,管理者権限で実行しなかったら,どうなるか?
というのも,ほんの一部ですが,書いています.
しかし,基本的には,そういうのは書かない方針です.

科学技術の進歩というのは,一見してくだらないと思える,あるいは疑問に
思うことを追及することで発展してきたのです.
ですから,本来であれば,現象を追及する方がいいのかも知れません.
プログラミングも同じだとは思います.本当はですが...  (^^;

> こんな事にマスターを巻き込んでしまいスミマセンでした。

地球は,太陽の周りを回っています.
そして,太陽系は,われわれの銀河の渦巻きの中にいるのです.
好む好まざるとに関係なく,我々は銀河の運動に巻き込まれているのであります.

# 意味が違うって !!
# モーガン・フリーマンの番組の見過ぎだな,こりゃ..

では,ご健闘をお祈りいたします.
あまり力(リキ)入れ過ぎて,体を壊さないように.
精神は,もうすでに壊れていると思いますが... ワハハハッ.
『プログラマーは皆,廃人』


[9986] Re9:インスタンスの生成をどこでやるか返信 削除
2016/5/23 (月) 00:05:49 Nov
▼ takiさん
> 今作成中のプログラムも「varなし」の方法に書き換えていきます。

なんか言葉がいろいろ足りなくて、スミマセン。

伝えたかったのは、もっと単純な話です。
簡潔なコードをサクッと書くスキル(と根性)が足りないのでご容赦を。

(1) 動的配列をvarのない引数としてメソッドに渡す場合
  メソッド内ではローカル変数ですから、SetLengthしてアドレスが変わると、
  元の配列を参照しなくなるので、値を変更しても呼び出し元の動的配列とは
  縁が切れています。

(2) 動的配列をvarで渡す場合
  呼び出し元のメモリ再配置が起こるような状況でも、配列に対する操作が
  常に有効になるので、それはそれで意味があるコードだと思います。
  実際、SetLength自体も参照渡しですし。

まぁ、基本何でもアリかな、と思うわけです。責任は持てませんけど。

[▼次のスレッド]
INCM/CMT
Cyclamen v3.82