Delphi面向对象学习随笔六:接口

本文详细介绍了接口的概念、定义方式、继承、实现、使用方法及其实现过程。重点阐述了接口如何通过提供操作规范,简化对象化编程中实体间的交互,实现核心理念的‘分离’,并通过Delphi语言的具体示例,展示了接口的强大功能和在对象化编程中的不可或缺性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


======================================================
注:本文源代码点此下载
======================================================

作者:巴哈姆特

(转载请注明出处并保持完整)

在对象化中,类的继承是一个非常强大的机制;而更加强大的继承机制应该是来自从一个接口的继承。

本篇我们将讨论接口的特点。

首先,接口的定义方式与类相似。不同的是:类代表了一种实体,而接口代表了一批操作规范。还有,接口中所有的数据成员都是public访问限制,也就是说,你不能为接口中的数据成员指定其为私有或其他的域成员。另外,接口中的方法只能有声明而不能有实现,因此它看上去更像是一个没有构造和析构方法的纯虚类。

我看的很多资料中,凡是在介绍接口的时候都会提到“多重继承”,仿佛接口的存在只是为了弥补object pascal不支持多重继承而设计的(至少给我的第一印象就是这样),其实接口是非常强大的,也是对象化编程中不可或缺的一个重要组成部分。

接口之所以强大在于:接口只需要告诉用户方法的名称是什么,有什么参数;而它并不需要理会方法是怎么实现的。例如电脑的构造和工作方式对于一般用户并不重要,因为一般用户更关心的是如何去使用他。所以电脑的接口——鼠标、键盘、显示器等才是用户最关心的地方。那么这就为我们实现对象化最核心的理念——“分离”提供了相当大的便捷。

首先我们来看看接口的定义方式:下面是delphi中system.pas里iinterface接口的声名方式

type

iinterface = interface

['']

function queryinterface(const iid: tguid; out obj): hresult; stdcall;

function _addref: integer; stdcall;

function _release: integer; stdcall;

end;

我们可以看到,和类的基本声明差不多,只是由关键字class改成了interface。

大家也许会注意到在紧跟在声明后的[''],这是什么呢?这是其实是接口的唯一标识,也就是我们说的tguid;当把接口注册给系统后,我们可以通过注册表检索到00000000-0000-0000-c000-000000000046这样的键值。那么这就意味着,我们只需要知道一个tguid的值就可以方便的访问这个接口。

当然,你可以在接口中定义其他的方法,但是delphi中是不允许给接口添加变量成员的,因为接口是不允许有实现部分的。

同样,接口也可以继承、封装以及方法的覆盖。继承接口同类继承类似:

type

inewinterface = interface(iinterface)

// 定义一个新的接口inewinterface,并告诉编译器它是继承自iinterface接口

....

end;

这里要注意一点,就是我们说过,接口的tguid是每一个接口的唯一标识,那么也就是说,tguid是不能重复的。你不能简单的从别处抄袭过来,那样是错误的。如果你需要一个tguid,你可以在delphi的代码编辑框中同时按下ctrl+shift+g来获得一个新的tguid值,这个值是delphi为你自动生成的。

接口的继承大致就是这样,和tobject是所有类的根类一样,在delphi中iinterface是所有接口的根类,那么类似于下面的继承:

type

inewinterface = interface

...

end;

这里要注意一点,就是我们说过,接口的tguid是每一个接口的唯一标识,那么也就是说,tguid是不能重复的。你不能简单的从别处抄袭过来,那样是错误的。如果你需要一个tguid,你可以在delphi的代码编辑框中同时按下ctrl+shift+g来获得一个新的tguid值,这个值是delphi为你自动生成的。

接口的继承大致就是这样,和tobject是所有类的根类一样,在delphi中iinterface是所有接口的根类,那么类似于下面的继承:

其实也是定义了一个继承自iinterface的新接口inewinterface(和类名前加一个大写字母t一样,我们习惯于在接口名前加一个大写字母i,当然这只是一个命名约定)

我们说了,接口只能有声明,不能有实现。那么怎么让接口为我们工作呢?

其实,接口的实现是需要借助于类来完成的(当然这看上去和c++中的多重继承的写法差不多)注意,既然接口是需要借助类来实现的,那么也就是说用来实现接口的类,必须实现接口中所有已定义的方法:

type

tnewinterfaceclass = class(tinterfacedobject, inewinterface)

// tinterfacedobject为类名,inewinterface为我们上面定义的接口名

...

end;

一般,我们用来实现接口的基类不会选tobject而会选tinterfacedobject,理由是tinterfacedobject类已经帮我们实现了iinterface接口中的方法,我们只需要实现我们自己接口中新的方法就可以了。

既然我们已经通过类实现了接口中的方法,那么我们就可以使用这个接口来为我们服务了,实例化接口也非常简单:

var

newface: inewinterface;

begin

newface:= tnewinterfaceclass.create(); // 创建接口

newface.xxx; // 调用接口中的方法

newface:= nil; // 释放

end;

有朋友可能会奇怪,接口的释放为什么只是直接赋为nil?我们前面说过了:接口即没有构造方法,也没有析构方法。既然没有析构方法,那么就意味着我们不能用释放类的方式来释放接口。

那么直接把接口对象指空会造成内存泄露吗?

答案是否定的,因为接口提供了一个引用记数的机制:当某接口实例(也就是实现了这个接口的对象)被引用,比如被赋值给一个接口变量时,该接口实例的addref方法会被编译器自动调用,引用计数将增加一。引用取消时编译器则会调用_release方法将引用计数减一。引用计数减到零时,表示已无其他接口变量引用此接口实例,此时编译器会自动释放它。

要注意的是,接口对象与类对象是不能混用的。当然像我们上面的例子里,newface也只能调用inewinterface接口中所定义过的方法,而不能调用类中定义的不存在于接口中的方法。

当然,一个类可以同时实现多个接口,多个接口彼此用逗号隔开。如:

type

tnewinterfaceclass = class(tinterfacedobject, iinterface1, iinterface2)

...

end;

同样,实现多个接口的类必须依次实现每个接口中定义的方法。

那么,这时出现了一个问题——就是当两个接口中有同名方法怎么办?好办,为他们取别名:

type

iinterface1 = interface(iinterface)

// 接口1

fucntion func(): boolean;

end;

iinterface2 = interface(iinterface)

// 接口2

function func(): boolean;

end;

tclasses = class(tinterfacedobject, iinterface1, iinterface2)

public

function iinterface1.func: func1;

function iinterface2.func: func2;

{ 为同名方法起别名 }

function func1: boolean;

function func2: boolean;

{ 声明方法 }

end;

delphi中还可以使用imploements指示符用于委托另一个类或接口来实现接口的某个方法,有时这个方法又被称为委托实现,关于implements的用法如下:

type

tinterclass = class(tinterfacedobject, iinterface1)

...

function getclasses: tclasses;

property face: tclasses read getclasses implements iinterface1;

...

end;

上面的代码中,implements指示字会要求编译器在face属性中寻找实现iinterface1接口的方法,属性的类型必须是一个类或一个接口。implements可以指定多个接口,彼此用逗号分隔。

implements指示字的好处是:

一、他允许以无冲突的方式进行接口聚合。(聚合是com中的概念)

二、他能够延后占用实现接口所需要的资源,直到确实需要资源。

下面是关于委托的一个详细例子(该例在delphi7 + win2000 sp4 中调试通过):

inewinterface = interface(iinterface)

// 定义接口

function sayhello: string; stdcall;

// 接口方法

end;

tnewclass = class(tinterfacedobject, inewinterface)

public

function sayhello: string; stdcall;

// 第一个类实现接口中的方法

end;

tnewclass1 = class(tinterfacedobject, inewinterface)

private

fnewclass: inewinterface;

public

// 注意,在这个类中并没有实现接口中的syahello方法

constructor create;

destructor destroy; override;

property newclass: inewinterface read fnewclass implements inewinterface;

// 接口对象委托 如果是类对象委托应该是

// property newclass: tnewclass read fnewclass implements inewinterface;

end;

implementation

function tnewclass.sayhello: string;

begin

result:= classname;

showmessage(result);

end;

constructor tnewclass1.create;

begin

inherited create();

fnewclass:= tnewclass.create;

// 在构造方法中创建接口

end;

destructor tnewclass1.destroy;

begin

fnewclass:= nil;

// 在析构方法中释放接口

inherited destroy();

end;

调用的例子:

var

newinterface: inewinterface;

begin

newinterface:= tnewclass1.create;

newinterface.sayhello;

newinterface:= nil;

end;

题外话:如果你还没有接触过com/com+的话,也许你会认为接口十分麻烦(ps: 当年我刚学的时候真想一脚把发明接口机制的人踹死),但是接口经过com的封装后,将变的非常的有意义,呵呵!


======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值