http://blog.csdn.net/acdnjjjdjkdckjj/article/details/6425119
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;
- }
首先来分析上述代码的类继承结构
IUnknown是原始抽象基类, Derived1和Derived2都是从IUnknown派生的(两个都是接口),A 从Derived1和Derived2继承的。
现在我们来看看三个类的对象模型布局:
从图可以看出各个接口的虚函数表, 值得注意的是类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原理与应用>>