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

Delphi/interfaceを使おう

はじめに Edit

なぜ、Delphi で interface(インターフェイス)は使わねーよ!なのか?

それは、Delphiでは、普通にOOPするのと、interfaceのプログラミングを混ぜて使うと、解放処理が紛らわしいからです。

問題となる例1 Edit

COMじゃない(Java風の)intefaceを使う場合は、Delphiの6あたりから導入された、TInterfacedObjectを使うと思うのですが、これがやっかいです。

下記、コードを見て下さい。

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
program test1_no;
{$APPTYPE CONSOLE}
uses SysUtils;
type
  ITest = interface
    procedure Print;
  end;

  TTest = class(TInterfacedObject, ITest)
    procedure Print;
    destructor Destroy; override;
  end;

{ TTest }
destructor TTest.Destroy;
begin
  Writeln('Destroy!!!!');
  inherited;
end;

procedure TTest.Print;
begin
  Writeln('Hello!!');
end;

procedure CallPrint(Test: ITest);
begin
  Test.Print;
end;

procedure Main();
var Test: TTest;
begin
  Test := TTest.Create;
  CallPrint(Test);
  Test.Free;
end;

begin
  ReportMemoryLeaksOnShutdown := True;
  Main;
end.

実行結果

Hello!!
Destroy!!!!
Destroy!!!!
EInvalidPointer がモジュール test1_no.exe の 00003861 で発生しました。
無効なポインタ操作.

普通のクラスをinterface対応にしようと、TInterfacedObjectを利用したところ、interfaceを引数に持つ関数を呼び出し、解放すると、
二回解放され(Destroyが二回呼ばれ)て死亡します。

解決策は、Freeを明示的に呼ばないことですが、
interfaceを引数に渡したかどうか、でFreeを呼ぶか呼ばないか、など考えていられませんよね。

また、もう一つの解決策は、インスタンスの型宣言をinterfaceにしてしまうことです。

つまり、

var Test: TTest;

var Test: ITest;

にしてしまうことです(interfaceだから、Freeももちろん呼ばない)

この場合は、普通にオブジェクトとして振舞えませんので、
「既存のオブジェクトをそのままinterfaceにしたい」って時は使えませんし、使いたいメソッドやプロパティすべてをinterfaceにしないといけないなど、激しく面倒くさいです。

この変の面倒くささが、Delphiのinterfaceの使えない理由でもあります。

そこで、解決例。

解決例1 Edit

そこで、../必須のサポートライブラリを使った例。
これで、interfaceは引数に渡しても勝手に解放はされず、
Freeを呼んでも、二重に解放されません。
型もクラスの型でよいし、既存のオブジェクトと同じように扱えます。

というか、なんで、CodeGearは、これを最初から用意しておかないのだろうか、という……。

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
program test3_ok;
{$APPTYPE CONSOLE}
uses
  SysUtils,
  InterfaceUtilsUnit in 'InterfaceUtilsUnit.pas';

type
  ITest = interface
    procedure Print;
  end;
  TTest = class(TInterfacedBase, ITest)
    procedure Print;
    destructor Destroy; override;
  end;

{ TTest }
destructor TTest.Destroy;
begin
  Writeln('Destroy!!!!');
  inherited;
end;

procedure TTest.Print;
begin
  Writeln('Hello!!');
end;

procedure CallPrint(Test: ITest);
begin
  Test.Print;
end;

procedure Main();
var Test: TTest;
begin
  Test := TTest.Create;
  CallPrint(Test);
  Test.Free;
end;

begin
  ReportMemoryLeaksOnShutdown := True;
  Main;
end.

実行結果は、以下、

Hello!!
Destroy!!!!

OK!ヽ( ´ー`)ノ




Front page   Edit Freeze Diff Backup Upload Copy Rename Reload   New Pages Search Recent changes   Help   RSS of recent changes
Last-modified: 2007-09-26 Wed 19:39:22 JST (3736d)