下面代码演示多接口的概念
class IMath
{
public :
virtual int Add(int a, int b) = 0;
virtual int Subtract(int a, int b) = 0;
virtual int Divide(int a, int b) = 0;
virtual int Multify(int a, int b) = 0;
}
class IAdvancedMath
{
public :
virtual int Factorial (int16_t n) = 0;
virtual int Fibonacci (int16_t n) = 0;
};
如果你要打算改变你的接口:在接口中添加 新的方法,或者改变接口中已有方法的参数。这样是不应该的,因为客户程序在编译的时候已经同接口的虚表绑定了,比如上面的IAdvancedMath。所以这里建议你再添加一个新的接口 :IAdvancedMathEx. 像下面这样
class IAdvancedMathEx : public IAdvancedMath
{
virtual int Circle(int radius) = 0;
}
那么上面我们描述了COM的接口的原理,具体的实现的细节等一下再讲,那么对于使用组件的客户端怎么来访问呢?
IMath* pIMath;
CoCreateInstance(CLSID_MATH, NULL, CLSCTX_INPROC, IID_IMATH, &pIMath);
这里有很多细节问题,但是最重要的,COM在这里创建了一个实例, 并返回了一个指向vtable结构指针的指针。该table结构是由抽象类IMath描述的。所以我们可以调用这个抽象类暴露出的方法。
大多数组件都有多个接口,系统将使用某种机制来实现,使客户可以访问某个特定的接口。
标准COM接口的一些细节
1.所有组件必须实现IUnknown接口,提供了一种标准途径,使用户可以在指定的组件访问某个特定的接口---这就是上文提到的COM的某种标准。组件对象的生存期管理
下面这是它的接口原型:
class IUnknow
{
virtual HRESULT STDMETHODCALLTYPE QueryInterface(
/* [in] */ REFIID riid,
/* [iid_is][out] */ __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject) = 0;
virtual ULONG STDMETHODCALLTYPE AddRef( void) = 0;
virtual ULONG STDMETHODCALLTYPE Release( void) = 0;
};
当客户需要使用组件提供的服务的时候,可以通过接口查询或者组件实例化的时候得到接口指针。
HRESULT STDMETHODCALLTYPE Math::QueryInterface(
/* [in] */ REFIID riid,
/* [annotation][iid_is][out] */
__RPC__deref_out void **ppvObject)
{
if (IID_IMATH == riid)
{
*ppvObject = static_cast<IMath*>(this);
}
else if (IID_IADVANCEDMATH == riid)
{
*ppvObject = static_cast<IAdvancedMath*>(this);
}
else if (IID_IUnknown == riid)
{
*ppvObject = static_cast<IMath*>(this);
//or *ppvObject = static_cast<IAdvancedMath*>(this);
}
else
{
return E_FAIL;
}
static_cast<IUnknown*>(*ppvObject)->AddRef();
return S_OK;
}
在queryinterface的一个实现中,我们可以看到它完整的实现了我们对该接口定位的功能。