2011年11月14日月曜日

DELPHIでInterfaceを使ってみる(2)

前回ではInterfaceの持つ参照カウンター機能について書きました。他にもIntefaceには「特定のメソッド」を持つオブジェクトをグループ分けする使い方があります。たとえば以下のようなInterfaceを用意します。




  • インターフェースオブジェクトのメモリ管理




  •   IType = interface(IUnKnown)
        function GetTypeName() : string ;
      end;
    
    これを実装したクラスを用意します。
      THuman = class(TInterfacedObject,IType)
        ....
        function GetTypeName() : string ; // get 'Human'
      end ;
    
      TAnimal = class(TInterfacedObject,IType)
        ....
        function GetTypeName() : string ; // get 'Animal'
      end ;
    
      TFish = class(TInterfacedObject,IType)
        ....
        function GetTypeName() : string ; // get 'Fish'
      end ;
    
    これらを利用して次のようなTypeNameを取得する関数を作りたいと考えます。
    function GetTypeName(intf : IType) : String ;
    begin
      exit(intf.GetTypeName()) ;
    end;
    
    GetTypeName(...)関数は引数のintfからGetTypeName()を実行し、TypeNameを取得したいと考えいます。たとえば、以下のようにTHumanのインスタンスオブジェクトを引数に入力すると、'Human'と返すなどです。しかし、ここで問題が分ります。
    human := THuman.Create() ;
    Writeln(GetTypeName(human)) ; // print out 'Human'
    ......
    
    ここで使っているInterfaceは参照カウンタ付きのInterfaceのため、GetTypeName(...)関数の中でhumanオブジェクトは解放されてしまいます。となると、GetTypeName(...)を抜けると、humanは解放されているため、混乱や問題が生じる可能性があります。
    このような場合は参照カウンターを無効にしたInterfaceを使えば解放される心配はありません。

    DELPHIには参照カウンターが無効になるよう実装しているクラスがありませんので、自分で実装します。
    (2011/11/24 参照カウンターが無効になるように実装しているクラスがDELPHIには用意されていました - TSingletonImplementation というクラスでGenerics.Defautsにあります)

    次のような感じでしょうか。
      TNonRefInterfacedObject = class(TObject, IInterface)
      protected
        function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
        function _AddRef: Integer; stdcall;
        function _Release: Integer; stdcall;
      end;
    
    ...
    
    function TNonRefInterfacedObject.QueryInterface(const IID: TGUID;
      out Obj): HResult;
    begin
      if GetInterface(IID, Obj) then
        Result := 0
      else
        Result := E_NOINTERFACE;
    end;
    
    function TNonRefInterfacedObject._AddRef: Integer;
    begin
      exit(-1) ;
    end;
    
    function TNonRefInterfacedObject._Release: Integer;
    begin
      exit(-1) ;
    end;
    

    この場合、_AddRefが呼ばれても_Releaseが呼ばれても参照カウンターは変化しません。

    次ももう少しだけDELPHIのInterfaceを使ってみます。


    ソースコードは自由にご使用ください。ただし問題が起きても責任はとれません。

    0 件のコメント:

    コメントを投稿