C++ Builder中使用接口

这是一个我想骂人的话题,但我还是忍住装一把文明人吧!

首先说问题的来源,天地弦让我帮着看一个在C++ Builder调用QueryInterface的问题,结果发现郁闷的紧,总报异常,要么就是提示调用了纯虚函数,找来找去,原来需要自己实现一遍AddRef和Release函数,瞬即蛋疼。

好在EMB有一点良心吧,在systobj.h里创建了一个宏INTFOBJECT_IMPL_IUNKNOWN,但却蛋疼的由于QueryInterface与基类的声明形式不一致,造成两个警告,所以自己实现一个宏定义:

#define INTF_IMP_REFCOUNT(BASE) \
 ULONG __stdcall AddRef() { return BASE::_AddRef();} \
 ULONG __stdcall Release(){ return BASE::_Release();}\
 HRESULT __stdcall QueryInterface(REFIID iid, void*p) { return BASE::QueryInterface(iid, p);}

在相应接口的定义里加入它就OK,如TForm1我们实现了接口ITest,那么代码参考如下:

class TForm1:public TForm, public ITest
{
...
public:
   INTF_IMP_REFCOUNT(TForm)
};

然后再调用TForm1的QueryInterface就没有问题了。

现在总结一下C++ Builder中使用接口的基本规则:

1、首先,要通过QueryInterface来获取一个接口,那么我们就必需保证实现了AddRef/Release函数,因为你的TInterfacedObject很遗憾的没有实现AddRef/Release,TInterfacedObject实现的是_AddRef/_Release,Delphi调用的它,但CB却没有,所以你需要自己加入AddRef/Release的实现。

2、C++ Builder的systobj.h实现了一个TCppInterfacedObject,只能说鸡肋。首先是VCL对象不允许多重继承,像TForm啥的现成的组件上,你要继承实现一个接口,就没法继承自它,实际上你还是需要重写它。重写也就重写吧,结果两个接口还不一致,造成警告。

3、所有的接口必需继承自IUnknown,否则QueryInterface会傻掉。

4、如果你是类的实例地址,那么直接用dynamic_cast<接口类型*>(对象实例)要比QueryInterface好的多,因为在C++里,所有的接口是纯虚函数,所以子类肯定可以转换成父类。

5、如果你得到的是IUnknown的接口实例,可以用interface_cast或者用QueryInterface方法了。

简单附一下interface的语法:

interface/struct declspec(uuid(GUID编码字符串)) 接口类型名称:public 父接口类型
{
virtual 函数名(函数参数)=0;
...
};

实际上在C++中,interface也不过是struct,所以两者写成啥都行。

 

分享到: