一个COM对象中,不同接口的函数名称不能相同,变通变通……(100分)

  • 主题发起人 主题发起人 op
  • 开始时间 开始时间
O

op

Unregistered / Unconfirmed
GUEST, unregistred user!
一个COM类的多个接口就相当于这个类的多个纯虚父类,
如果多个纯虚父类都声明了一个相同名称的纯虚函数,
当这个函数在子类中被实现,并被这些父类调用时,
它如何判断调用自己的是哪个父类?
 
当为一个类实现一个接口时,它必须逐个实现接口的每个对象方法
因此,我觉得并非像OO中那样调用父类的方法(OO中,这种情况的调用有一定顺序)
 
使用指示字implement可以指定实现的是哪个接口
 
采用方法解析语句,否则Delphi认为所有接口的同名函数采用相同的实现。
方法解析格式:
function IInterface1.GetName: WideString = GetName1;
function GetName1: WideString;
这样,IInterface1的GetName方法采用GetName1来实现,而没有采用方法解析
语句的IInterface2仍使用GetName的实现。
 
langer果真是高手,居然如此轻而一举就破解了。
唉,delphi就是delhpi,居然有自己的一套语法来实现这个功能。
可是我还是笑不出来,因为我用的是C++Builder……
langer如果用C++Builder就好了:<
看来COM是支持不同接口的函数名重载的,而且肯定不是采用我刚才的那个变通的办法。
我想COM内部一定是采用类似下面的办法来实现的:
如果有一个COM对象,我称它为TCOMObject,
IUnknown
IInterface1
IInterface2
是这个COM对象的3个接口。
TCOMObject_QueryInterface
TCOMObject_AddRef
TCOMObject_Release
TCOMObject_MyFunction1
TCOMObject_MyFunction2
是这个COM对象内部的5个函数。
那么,当IInterface1和IInterface2的第三个函数名都是MyFunction时,
一定是用下面的代码来指向不同的函数地址。
*(IUnknown+0)=(void(__stdcall*)())TCOMObject_QueryInterface;
*(IUnknown+1)=(void(__stdcall*)())TCOMObject_AddRef;
*(IUnknown+2)=(void(__stdcall*)())TCOMObject_Release;
*(IInterface1+0)=(void(__stdcall*)())TCOMObject_QueryInterface;
*(IInterface1+1)=(void(__stdcall*)())TCOMObject_AddRef;
*(IInterface1+2)=(void(__stdcall*)())TCOMObject_Release;
*(IInterface1+3)=(void(__stdcall*)())TCOMObject_MyFunction1;
*(IInterface2+0)=(void(__stdcall*)())TCOMObject_QueryInterface;
*(IInterface2+1)=(void(__stdcall*)())TCOMObject_AddRef;
*(IInterface2+2)=(void(__stdcall*)())TCOMObject_Release;
*(IInterface2+3)=(void(__stdcall*)())TCOMObject_MyFunction2;
然而,在实际中,用C++Builder是如何实现的呢?
 
To op:
你用C++,发现与DELPHI 5 有何不同吗?
能说说吗?
我常将用C++做的东东COPY到DELPHI 5中来,
因我的对DELPHI 5较会用些。
再者,我认为DELPHI 5的OOP与C++的没什么不同。
 
差别当然有,而且不少,其它的不说,单OOP的差别就有以下.....
1、BCB可Public/Protected/private的继承父类,而D只能Public的继承父类。
(如果只能Public的继承父类,将导致子类一定大于父类,而不能屏蔽父类的方法,这对面向对象来说是不利的。)
2、BCB可真正的写自己的类,而不用一定要从TObject来继承。
(在写一个小型的类时很方便,而且实例可以生成在栈里,而不是在堆里。)
3、BCB类与简单类型用法一致,无须特殊对待。
4、D中,一个Unit文件里不同Class的变量可共访。
(这将导致编程“语言”与物理“文件”的相关性。)
5、BCB可支持多重继承。
 
C++Builder我不是很熟,但是既然C++Builder也提供了源码,就看看它是如何
解决最基本的方法QueryInterface,AddRef及Release吧,我想你一定可以找到
答案。
 
To op:

你说的很详细,我受益非浅,我只用DELPHI用得多,C++少用,
所以算是个门外汉了,看来是得好好的学学C++了,谢谢你。
我很少看源码,所以要是考OOP的话,可能不会及格,我编的
软件多半是堆积木样的堆出来的。
 
op,Delphi对COM对象接口函数地址的计算是在编译阶段,因此接口实现的方式很灵活。
具体的你可以看看TObject.GetInterfaceEntry的实现(用汇编写的)。
另外D4以后引入的implements关键字更是妙处多多,不仅把COM对象的实现和传统的面向对象完美的结合了起来,更是自然的实现了COM的聚集,实在是方便之至。
btw,传统面向对象的多重继承是弊大于利,而COM基于接口的设计加上包容和聚集的代码重用机制才真正解决了问题。
 
建议看看Delphi的TComObject类是怎样在不同的情况下使用IUnknown的不同实现吧(一套是为非聚集组件写的,一套则考虑了聚集的情况)。
细想下来,利用Delphi的接口特性,即使不编写COM组件,也可以更清楚的实现扁平式的高效率的多重继承——而没有C++多重继承所有弊端的COM的多重接口继承。
再加上implements和COM的聚集,COM因此实现了代码的二进制多重继承。
 
铁马小子:
Delphi对COM对象接口函数地址的计算是在编译阶段,
这里的函数地址具体指的是什么?是类似于:
*(IUnknown+0)=(void(__stdcall*)())TCOMObject_QueryInterface之类的计算
吗?是不是函数在该COM对象里的相对地址?
能讲一讲implements关键字的使用吗?
谢谢!
 
Delphi通过“包容和聚集”来实现二进制上的多重继承,的确不错。
不过这不是Delhpi的优点,而是COM的功劳。任何开发工具都支持。
不知铁兄为什么说OOP的多重继承弊大于利?
我个人倒认为COM的这种包容和聚集有些牵强。
就好像TFrom里new一个TButton,然后输出TButton的所有方法,就说TForm继承了TButton。
(COM就是因为不支持真正的"继承"而被人当作笑柄)。
其实OOP和COM的多重继承方式各有所长,一个更灵活,一个更宏观。

哦,我的那个问题终于找到C++方式的答案了。其实我最初的想法就已经离答案不远了。
一个COM类的多个接口就相当于这个类的多个抽象父类,
如果多个抽象父类都声明了一个相同名称的纯虚函数,
那么这些函数在其相应的子类中被实现就可以了。
我原来的想法是这些纯虚函数在TCOMClass中实现,如Delhpi和BCB的默认方式:
IUnKnown
|
IDispatch
|
|-----------------|-----------------|
IInterface1 IInterface2 IInterface3
| | |
|-----------------|-----------------|
|
TCOMClass
//////////////////////////////////////////////////////////
用下面这种方式,同名的纯虚函数在各自的IXXX_Impl中实现,我的问题就解决了:
IUnKnown
|
IDispatch
|
|-----------------|-----------------|
IInterface1 IInterface2 IInterface3
| | |
IInterface1_Impl IInterface2_Impl IInterface3_Impl
| | |
|-----------------|-----------------|
|
TCOMClass

其实我认为这种方式更好,以后我可以把各自的接口放在各个Unit中实现,
这样有利于大型项目的开发。
 
后退
顶部