喫茶XRAYトップページ

[スレッド全体
]

[9927] 今更きけない 動的配列…(いや、まぁ聞くんだけどさ)返信 削除
2016/5/15 (日) 19:47:35 taki
マスター 教えてください。
動的配列を使う度に、以下の記事が気になって心配になります。

------------------------------------------
Delphi/動的配列の罠
http://tobysoft.net/wiki/index.php?Delphi%2F%C6%B0%C5%AA%C7%DB%CE%F3%A4%CE%E6%AB

動的配列を「string のようなものだ」と思っていると痛い目に合います。
動的配列は参照カウンタつきポインタ

「代入したときは参照カウンタをインクリメント」してくれ、
「スコープが外れたときは、参照カウンタをデクリメントし、
  0になったら自動的に開放」はしますが、
「書き換えた時にコピー(copy on write)」はしてくれないのです。
------------------------------------------

説明はヘルプの「動的配列」のところに書いてあるそうなのですが、
Delphi XP4 Starter のHELPでキーワードに動的配列を入力しても説明が
見つかりません。

記事の説明のコードを見ると、単純に := で代入をするとポインタが
コピーされるので、「代入元と代入先が同じメモリーを参照します」と
読めるのですが、他に何か罠があるのでしょうか?

言葉で分からないのは「参照カウンタつきXX」の部分です。


[9932] Re:今更きけない 動的配列…(いや、まぁ聞くんだけどさ)返信 削除
2016/5/16 (月) 12:07:50 Mr.XRAY
▼ takiさん

> マスター 教えてください。

分かりません !! (キッパリ)

> Delphi/動的配列の罠
> http://tobysoft.net/wiki/index.php?Delphi%2F%C6%B0%C5%AA%C7%DB%CE%F3%A4%CE%E6%AB


あっ,これ,面白い !

>だから、「動的配列はポインタだ」という事だけ気をつけておけば
>いいのでしょうか?


だからか,どうかは分かりませんが (^^;
私的な立場では... ホホホッ.


[9942] 動的配列とポインタと型と...返信 削除
2016/5/16 (月) 18:19:38 Mr.XRAY
> >だから、「動的配列はポインタだ」という事だけ気をつけておけば
> >いいのでしょうか?
>
> だからか,どうかは分かりませんが (^^;
> 私的な立場では... ホホホッ.


誤解されると困るので,一応.
以下も動的配列といいますが,これは,代入できません.
([DCC エラー] Unit1.pas(64): E2008 互換性の無い型です) となります.

procedure TForm1.Button1Click(Sender: TObject);
var
  Array1 : array of Integer;
  Array2 : array of Integer;
begin
  // array1の確保と変更
  SetLength(Array1, 10);
  SetLength(Array2, 10);

  Array2 := Array1;  //これはエラー


この代入が可能なのは,type 節で定義した型やクラスのインスタンスです.
(コンポーネントもクラスです)
または,String 型や Integer 型のような単純な型 (?) の同じ型の変数同志です.
例えば,TIntegerDynArray は次のように定義されています.

type
  TIntegerDynArray = array of Integer;


ちょっ前のスレッドにありましたが,HQUERY もそうです.
HQUERY は Type で宣言されています.そのため,型が違うというエラーに
なったわけです.

type で前言した「型」は,実はポインタということですね.


[9946] Re:動的配列とポインタと型と...返信 削除
2016/5/16 (月) 20:07:16 taki
▼ Mr.XRAYさん

手続き(関数)の引数として動的配列を使うときに注意が必要という事みたいです。

type
  TTestAry = array of integer;

procedure ABC(buf:TTestAry);
begin
  Form1.Label1.Caption := inttostr(buf[0]);   // 999
  // 何の気なしに手続き内で値を変更する
  buf[0] := 888;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Abuf:TTestAry;
begin
  SetLength(Abuf,1);
  Abuf[0] := 999;
  // 手続きを呼び出す
  ABC(Abuf);
  // Abufの値が変化してしまう
  Label2.Caption := inttostr(Abuf[0]);   // 888
end;

これって
procedure ABC(var buf:TTestAry); と同じですよね。

『var』と書いていないのに var 扱いになる!!
踏んじゃいそうですね。



[9962] Re2:動的配列とポインタと型と...返信 削除
2016/5/19 (木) 19:44:56 taki
> これって
> procedure ABC(var buf:TTestAry); と同じですよね。
>
> 『var』と書いていないのに var 扱いになる!!
>


関数(手続き)内で配列のサイズを指定してデータをセットする場合、
「Varなし」と「Varあり」とでは働きがちがうようです。

type
  TTestAry = array of integer;

// Varなし手続きの場合
procedure ABC(buf:TTestAry);
begin
  SetLength(buf,1);       // 手続き内で配列のサイズを決定
  buf[0] := 999;
  Form1.Label1.Caption := inttostr(buf[0]);   // 999
  // 手続き内で値を変更する
  buf[0] := 888;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Abuf:TTestAry;
begin
  // 手続きを呼び出す
  ABC(Abuf);
  Label2.Caption := inttostr(Abuf[0]);   // Abuf は nil で、エラーになる
end;

///-------------------------------------------

// Varあり手続きの場合
procedure VarABC(var buf:TTestAry);
begin
  SetLength(buf,1);       // 手続き内で配列のサイズを決定
  buf[0] := 999;
  Form1.Label1.Caption := inttostr(buf[0]);   // 999
  // 手続き内で値を変更する
  buf[0] := 888;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  Abuf:TTestAry;
begin
  // 手続きを呼び出す
  VarABC(Abuf);
  Label2.Caption := inttostr(Abuf[0]);   // 888
end;

えっ、もうややこしいこと言うなって?

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


[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 を指定すると,
# 挙動の違いにより,トラブルが発生した時に面倒になるかも知れません.


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