前回までのソースコードの抜粋です。
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 件のコメント:
コメントを投稿