- お知らせ -
  • 当wikiのプログラムコードの表示を直してみました(ついでに長い行があると全体が下にぶっ飛ぶのも修正)。不具合があればBBSまでご連絡下さい。

環境と関数の組み合わせが動的クロージャとのことだが、
特筆すべきことなのかよくわからん。
ようは、delegateのことではないのか?

あ、そうか、delegateはあくまで委任のことで、
環境と関数の組み合わせが、delegateとして実装されているに過ぎない、のかな?

クロージャという言葉に反して、
宣言されている箇所を過ぎると無効になるのは、
動的たる所以でしょうか。
うーむ。

D Edit

とりあえず、環境+関数のコードということで、
classのフィールドにアクセスするコードと、
関数の外側のスタック変数にアクセスするのをば。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Foo
{
  int bar = 100;
  int xyzzy() {
    return bar;
  }

  ~this() {
    printf("Foo.~this()\n");
  }
}

void print(int delegate() func)
{
  printf("%d\n", func());
}

void main(char[][] argv)
{
  int delegate() funcA, funcB;
  
  void testClosure() {
    int a = 10;
    int b() {
      return a;
    }
    
    print(&b);
    funcA = &b;
    
    Foo foo = new Foo;
    print(&foo.xyzzy);
    funcB = &foo.xyzzy;
  }

  testClosure();
  
  print(funcA); // ダメポ
  print(funcB); // testClosure.fooが解放されていないのでOK
}

出力例

10
100
3933124
100
Foo.~this()

3933124はデタラメな値。
print(funcA);が不正。
関数を抜けた時点で、スタック変数(testClosure.a)が無効になっているわけ。

それに反して、
print(funcB);は有効。
これは、関数を抜けてもfooが生きているため。

ちなみに、Foo fooをauto宣言すると、
関数抜けた時点でfooが解放されるので、
print(funcB); で Access Violationが起こる。

Delphi Edit

メソッドポインタを利用した例 Edit

関数内関数のポインタとメソッドポインタは互換性がないので、
メソッドポインタの例だけ。
ちと不便。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
program main;

{$APPTYPE CONSOLE}
uses SysUtils;
type
  TFunc = function: Integer of object;
  TFoo = class
    FBar: Integer;
    constructor Create;
    function Xyzzy: Integer;
  end;

constructor TFoo.Create;
begin
  FBar := 100
end;

function TFoo.Xyzzy;
begin
  Result := FBar
end;

procedure Print(Func: TFunc);
begin
  Writeln(Func);
end;

var
  Func: TFunc;

  procedure testClosure;
  var Foo: TFoo;
  begin
    Foo := TFoo.Create;
    try
      Print(Foo.Xyzzy);
      Func := Foo.Xyzzy;
    finally
      FreeAndNil(Foo);
    end;

    Foo := TFoo.Create;
    try
      Foo.FBar := 9999;
    finally
      FreeAndNil(Foo);
    end;
  end;
begin
  testClosure;
  
  Print(Func);  // TFooが開放されているので、やっちゃ駄目です
end.

出力例

100
9999

Funcが、開放されたメモリを差しているので、
後で、上書きした値の9999が出る(ためしにやってみただけ)
実際には、バグの温床になるので、こんなことやってはいけません。




Front page   Edit Freeze Diff Backup Upload Copy Rename Reload   New Pages Search Recent changes   Help   RSS of recent changes
Last-modified: 2005-07-04 Mon 16:54:45 JST (5272d)