接口有如下特征:
.接口被声明为Interface类型,不是class类型。惯例是:接口名从字母I开始,正如类类型名从字母T开始。
.所有的接口都从IUnknown直接或间接继承。就像TObject是Delphj中所有类的基类一样,IUnknown是coM中所有接口的基础。
.接口不能自我创建和实现,必须有其派生的类来创建实例和实现功能。下列是错误的:
- var
- FormattedNumber:IFormattedNumber;
-
- FormattedNumber := IFormattedNumber.create(); //这将导致编译错误
- FormattedNumber := TFormattedNumber.create(); //TFormattedNumber类是IFormattedNumber接口
的派生实现,这是正确的。
1.不能创建接口实例。下面的代码是非法的;
1)不能在接口中指定范围指示。接口定义的所有方法都是公有型(public),不能在接口声明中包括范围指示(包括公有型)。
2)接口不能声明变量。接口只能决定提供什么样功能。对于如何完成该功能没有限制。如果允许在接口声明中放一个成员
变量,就会在某种程度上命令用何种方法来完成想要的功能。
3)在接口中声明的所有函数和过程,概念上讲都是虚(virtual)抽象函数和过程。声明时不必带virtual关键字;
事实上,这样做是非法的。
2.接口是标准协议,一旦定义经不能随便修改,如果要增强接口功能,可以派生新的接口:
例如:要为IFormattedNumber接口增加一个SetCaption函数
- IFormattedNumber2 = inerface(IFormattedNumber);
- procedure SetCatpion(ACapiton:String);
- end;
3.声明一个接口:
- IFormattedNumber = interface
- [GUID] //16字节,无重复标识符,在delphi中可Ctrl+Shift+G自动创建一个,在内部调用了coCreateGuid里产生GUID
- function formattedString:String;
- end;
- { //动态获取GUID的方法
- procedure TForm1.btnGenerateClick(Sender: TObject);
- var
- Guid: TGUID;
- begin
- CoCreateGuid(Guid);
- Memo1.Lines.Add(GuidToString(Guid));
- end;
- }
1)TGUID数据结构定义:
- type
- PGUID = ^TGUID;
- TGUID = packed record
- D1: Longword;
- D2: Word;
- D3: Word;
- D4: array[0..7] of Byte;
- end;
2)下面声明完全一样:
- const IID_IMalloc: TGUID ='{DC1D7C5F-C0DC-4056-B1F2-1D3A1ADA6D51}';
- const IID_IMalloc: TGUID =(D1:$DC1D7C5F;D2:$C0DC;D3:$4056;D4:($B1,$F2,$1D,$3A,$1A,$DA,$6D,$51));
3)接口的实现
- TFormattedNumber = class(TInterfacedObject,IFormattedNumber)
- private
- FormattedString:String;
- public
- function formattedString:String;
- end;
4)IUnkonwn接口
- IiInterface = interface
- ['00000000-0000-0000-C000-00000000000046']
- function QueryInterface(const IID:TGUID;out obj):HResult;stdcall;
- function _AddRef:integer;stdcall;
- function _Release:Integer;stdcall;
- end;
- IUnkonwn = IiInterface;
由此可见IUnkonwn是所有COM接口的基础接口,QueryInterface,_AddRef,_Release必须由IUnkonwn的派生类来实现,
在Delphi中由TInterfacedObject来实现了。
(1)QueryInterface
QueryInterface是请求指向一个接口指针的函数。如果接口是由问题中的对象实现的,QueryInterface返回在则
参数中的接口,且返回数值为0。如果接口不是由对象实现的,QueryInterface返回MicroSoft定义的
常量E_NOINTERFACE。
(2)_AddRef
接口是引用计数。因此在对象中当获得接口指针时,对象被引用的次数就增加了。当结束使用接口时,对象的
引用次数就下降了。当引用次数到达零时,就自动销毁对象。_AddRef是负责增大引用计数的函数。
如何物理上存储引用计数由读者决定,但是典型情况下将建立整型(Integer)变量去保存计数。
(3)_Release
_Release既是个负责降低引用计数的函数。当引用计数达到零时,就自动销毁对象。
5)TInterfacedObject实现了Iunkonwn接口的基本类
TInterfacedObject = class(TObject,IiInterface)
....
end;
6)多接口继承
一个类可以实现多个接口,但是如果这些接口中有同名函数,必须给他们取别名。
- type
- Ifoot = interface
- ...
- function F1:Integer;
- end;
-
- IBall = interface
- ...
- function F1:integer;
- end;
-
- TFootBall = class(TInterfacedObject,IFoot,IBall)
- //为同名方法取别名
- function IFoot.F1 = FootF1;
- function IBall.F1 = BallF1;
- //接口方法
- function FootF1:integer;
- function BallF1:integer;
- end;
7)接口的引用和销毁
Delphi在引用对象时:
- var
- FormattedNumber:IFormattedNumber;
- begin
- FormattedNumber := TFormattedNumber.create();
- end;
不必显示调用_Release()去释放资源。这是因为Delphi在后台已经帮你做了释放工作。如果是C或C++就必须显示调用。
如果要强制销毁一个接口:
- FormattedNumber := nil; //强制销毁接口,仅仅把该接口赋值为nil即可
8)获取接口的指针
Delphi提供了获取接口指针的一些方法。可以获得一个接口的指针,该指针给出了实现接口的一个对象,或给出了另
一个接口的指针。
(1)直接分配
从对象获取接口的最简单的方法是通过直接分配。类是与它们实现的接口类型兼容的,因此可以编写代码如下;
- //直接创建实现接口的类,直接赋值
- var
- FormattedNumber:IFormattedNumber;
- ForInteger: TFormattedInteger;
- begin
- ForInteger := TFormattedInteger.Create();
- FormattedNumber := ForInteger;
- //或者直接写为: FormattedNumber=TFormattedInteger.Create();
- end;
-
- //或直接从接口查询QueryInterface函数得到
- //function Tobject.GetInterface(const IID:TGUID;out:obj);Boolean;
- var
- MyObject :Tobject;
- MyNumber :IFormattedNumber;
- begin
- MyObject := TFormattedInteger.Create();
- if MyObject.GetInterface(IFormattedNumber,MyNumber) then //安全方式查询,不会产生异常
- begin
- showmessage(MyNumber.formattedString());
- end;
- end;
- 其实IUnkonwn接口中的QueryInterface正是调用了GetInterface方法来实现的接口查询:
- {
- function TinterfaceObject.QueryInterface(const IID:TGUID;out:Obj):HResult;
- const E_NOINTERFACE = $80004002;
- begin
- if GetInterface(IID,Obj) then result := 0 else Result:= E_NOINTERFACE;
- end;
- }
(2)as 操作符
as操作符即可在Tobject上使用,也可以在接口本身使用。
- var
- MyObject :Tobject;
- MyNumber :IFormattedNumber;
- begin
- MyObject := TFormattedInteger.Create();
- MyNumber := MyObject as IFormattedNumber; //如果MyObject与IFormattedNumber接口不匹配会有异常
- showmessage(MyNumber.formattedString());
- end;
(3)绕过引用计数器
Delphi为我们提供了一个绕过引用计数,不会自动销毁的类:
- TNonRefCountedObject = Class(TinterfaceObject,IUnkonwn)
- protected
- function _AddRef:integer;stdcall;
- function _Release:Integer;stdcall;
- end;
//例如:下面var A: Array of IUnknown参数如果换为var A: Array of TInterfaceObject;那么下面
//一行代码就使对象过早地销毁;
//if (A[J] as ICompare).CompareWith(A[I] as ICompare, ASortBy) < 0 then begin
//as操作符会使Delphi调用ICompare接口中的_AddRef,_Release,这样就会在排序过程中销毁排序的正确对象,
//这是一个混合引用模型时会产生的一个麻烦的经典实例。所以使用最基类的IUnknown类型可以避免这个问题。
- type
- ICompare = interface
- ['{DDFE0840-E8FB-11D2-9085-0040F6741DE2}']
- function CompareWith(ACompare: ICompare; ASortBy: Integer): Integer;
- end;
-
- procedure SortArray(var A: Array of IUnknown; ASortBy: Integer);
- var
- I, J: Integer;
- Temp: IUnknown;
- begin
- for I := Low(A) to High(A) - 1 do begin
- for J := I + 1 to High(A) do begin
- if (A[J] as ICompare).CompareWith(A[I] as ICompare, ASortBy) < 0 then begin
- Temp := A[I];
- A[I] := A[J];
- A[J] := Temp;
- end;
- end;
- end;
- end;
-
- end.
转载出处:http://blog.163.com/huangjian_w/blog/static/1943728120088118928775/
阅读(2025) | 评论(0) | 转发(0) |