2011年11月14日月曜日

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

前回はInterfaceを継承した場合を見てましたが、今回は実体であるクラスから派生したクラスでどうなるか見てみます。

前回までのソースコードの抜粋です。
  IType = interface(IUnKnown)
  ['{5CDFEE96-1683-4A40-A490-34D064DCAA18}']
    function GetTypeName() : string ;
  end;

  IIsBranchial = interface(IType)
  ['{B034BE0E-4087-4C45-B78A-CCF6FB7BF9B5}']
    function IsBranchial() : Boolean ;
  end;

  TNonRefInterfacedObject = class(TObject, IInterface)
  protected
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  end;

  THuman = class(TNonRefInterfacedObject,IIsBranchial,IType)
  public
    function GetTypeName() : string ; // return 'Human'
    function IsBranchial() : Boolean ; // false
  end;
THumanクラスではIType,IIsBranchialの両方明示的に継承しています。ですので、以下のような関数で使うとHumanと出力されます。

function GetTypeName(intf : IType) : String ;
begin
  exit(intf.GetTypeName()) ;
end;
...
    human := THuman.Create() ;
    Writeln(GetTypeName(human)) ;

出力
Human
ではTHumanから派生したTNewHumanではどうなるか試してみました。
  TNewHuman = class(THuman)

  end;
...
    human := TNewHuman.Create() ;
    Writeln(GetTypeName(human)) ;

出力
Human
派生元のクラスにおけるITypeインターフェイス実装が呼ばれました。
Intefaceの継承(IIsBranchial = Interface(IType)...)は場合は明示的に継承していないと呼ばれず、クラスの継承の場合は派生元のクラスで継承していれば呼ばれます。
その理由としてはvtable,VMTや仮想クラスなど勉強すれば分りそうです。後々の課題とします。また、インタフェースを継承するとその分インスタンスサイズが増えるので使う際には考えて設計する方がいいです。

次に、TNewHumanで各関数をオーバーライドした場合を考えます。
  THuman = class(TNonRefInterfacedObject,IIsBranchial,IType)
  public
    function GetTypeName() : string ;  virtual ; // return 'Human'
    function IsBranchial() : Boolean ; virtual ; // false
  end;

  TNewHuman = class(THuman)
  public
    function GetTypeName() : string ; override ; // return 'NewHuman'
    function IsBranchial() : Boolean ; override ; // false
  end;

出力
NewHuman
結果としてオーバーライドした関数の結果が返ってきました。Interface関数の実体における実装は仮想関数と同じ扱いだと考えてよさそうです。


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

0 件のコメント:

コメントを投稿