COM 组件对象的基本模仿

#include <iostream> #include <string> using namespace std; //类型定义和宏定义 typedef string REFIID; typedef unsigned long HRESULT; typedef unsigned long ULONG; #define S_OK 0 //接口查询成功返回值 #define E_NOINTERFACE -1 //接口查询失败返回值 string g_strIUnknown = "IID_IUnknown"; //IUnknown接口标志 string g_strDerived1 = "IID_ Derived1";// Derived1接口标志 string g_strDerived2 = "IID_ Derived2";//Derived2接口标志 //接口定义, 所有COM接口的原始接口,必须继承 class IUnknown { public: virtual HRESULT QueryInterface(REFIID iid,void ** ppvObject)=0; virtual ULONG AddRef(void)=0; virtual ULONG Release(void)=0; }; class Derived1:public IUnknown { public: virtual void FunDerived1()=0; }; class Derived2:public IUnknown { public: virtual void FunDerived2()=0; }; //A多继承 class A :public Derived1, public Derived2 { public: A(){m_ulRef = 0;} ~A(){} public: //IUnknown接口 virtual HRESULT QueryInterface(REFIID iid,void ** ppvObject); virtual ULONG AddRef(void); virtual ULONG Release(void); // Derived1继承的接口 virtual void FunDerived1(){cout<<"Derived1::FunDerived1"<<endl;} //Derived2继承的接口 virtual void FunDerived2(){cout<<"Derived2::FunDerived2"<<endl;} //自己定义的成员函数 void DisPlay(){cout<<"A::DisPlay"<<endl;} private: ULONG m_ulRef; //引用计数 }; HRESULT A::QueryInterface(REFIID iid,void ** ppvObject) { if (g_strIUnknown == iid) { //*ppvObject = (IUnknown*)this; // 因为是多继承,所以在组件对象中有两个Base对象,这种转换编译器会报错 *ppvObject = (Derived1*)this; //正确 } else if (g_strDerived1 == iid) { *ppvObject = (Derived1*)this; } else if (g_strDerived2 == iid) { *ppvObject = (Derived2*)this; } else { *ppvObject = NULL; return E_NOINTERFACE; } (Derived1*)this->AddRef(); return S_OK; } ULONG A::AddRef() { m_ulRef++; return m_ulRef; } ULONG A::Release() { m_ulRef--; if (m_ulRef == 0) { delete this; m_ulRef = 0; //注意这里要赋值为0 } return m_ulRef; } int main() { /************通过COM组件对象来调用*********/ A *pA = new A; pA->FunDerived1(); pA->FunDerived2(); pA->DisPlay(); /*通过COM 对象支持的接口来调用, 这个是COM 组件客户调用的规范,因为COM组件对象只暴露其支持的接口给客户*/ //1.通过接口IUnknown来调用 IUnknown* pIUnknown; pA->QueryInterface(g_strIUnknown,(void**)&pIUnknown); //2.通过接口Derived1来调用 Derived1* pDerived1; pA->QueryInterface(g_strDerived1, (void**)&pDerived1); pDerived1->FunDerived1(); //3.通过接口Derived2来调用 Derived2* pDerived2; pDerived1->QueryInterface(g_strDerived2, (void**)&pDerived2); pDerived2->FunDerived2(); //释放接口 pDerived1->Release(); pDerived2->Release(); if ((pIUnknown->Release()) == 0) { cout<<"组件已经从内存中卸出了!"<<endl; } return 0; }

 

首先来分析上述代码的类继承结构

 COM 组件对象的基本模仿_第1张图片

 

IUnknown是原始抽象基类, Derived1和Derived2都是从IUnknown派生的(两个都是接口),A 从Derived1和Derived2继承的。

现在我们来看看三个类的对象模型布局:

COM 组件对象的基本模仿_第2张图片

 

从图可以看出各个接口的虚函数表,  值得注意的是类A由于实现了所有的接口,所以虚函数表会有所改变。

所以当QueryInterface得到接口Derived1或者Derived2接口指针时候调用AddRef 或者Release函数时候都是调用A实现里面的相应版本,这就是所谓的动态绑定技术,当然组件对象必须是A类型的啦。

 

还有当查询IID_IUnknown接口时,如果QueryInterface是这样实现的

if (g_strIUnknown == iid)
 {
  *ppvObject = (IUnknown*)this; // 因为是多继承,所以在组件对象中有两个Base对象,这种转换编译器会报错
  }

编译器报错, 原因是:因为是多继承,所以在组件对象中有两个Base对象,造成二义性。这种转换编译器会报错

只要把 *ppvObject = (IUnknown*)this;改成*ppvObject = (Derived1*)this; 就行了。

 

当然本文只是用简单的C++ 来模型了一下COM 组件对象, 真正的COM 编程比这个要复杂点, 推荐大家去看看<<COM原理与应用>>

 

你可能感兴趣的:(编程,String,delete,Class,include,编译器)