COM组件有两种接口类型,Dual and Custom,如下图所示。本文说的是Custom。所谓多接口COM对象是指此COM对象实现了多于一个的自定义接口,即Custom接口。
接口图如下:
需要注意的是最终实现的COM对象用的不是虚继承而是普通的多继承,因为被多继承的IUnknown接口是不含任何数据成员,只有纯虚函数,继承的子接口也是这样。还有为了实现跨语言或平台的调用,最终没用虚继承。更多的原因请参考本文最后的链接。
这里给出多接口COM对象的模型:
很清楚地看到COM是实现了两个接口,也就是多继承子两个父接口,有两个虚函数表,其中IUnknown的三个函数指针在两个虚函数表里都各有一份。但是从我下面的打印结果来看指向的地址不太一样,不是很清楚真正的COM对象是怎么实现的。留着以后再研究吧。
示例程序。使用时会有assert dialog弹出来,直接点忽略就行了。
1 #include <iostream> 2 using namespace std; 3 4 class IUnknown 5 { 6 public: 7 virtual long QueryInterface( long riid, void * * ppvObject) = 0; 8 virtual long AddRef( void) = 0; 9 virtual long Release( void) = 0; 10 }; 11 12 class IMyMath : public IUnknown 13 { 14 public: 15 virtual long Add(long n1, long n2, long* pVal) = 0; 16 }; 17 18 class IStr : public IUnknown 19 { 20 public: 21 virtual long Cat(char* _Dest, const char* _Source) = 0; 22 }; 23 24 class CMyTest: public IMyMath, public IStr 25 { 26 public: 27 long QueryInterface( long riid, void * * ppvObject) { cout<<"QueryInterface"<<endl; return 1;} 28 long AddRef( void) { cout<<"AddRef"<<endl; return 1;} 29 long Release( void) { cout<<"Release"<<endl; return 1;} 30 31 long Add(long n1, long n2, long* pVal) { cout<<"Add"<<endl; return 1;} 32 long Cat(char* _Dest, const char* _Source){ cout<<"Cat"<<endl; return 1;} 33 }; 34 35 typedef long (*QueryInterfaceType)( long riid, void * * ppvObject); 36 typedef long (*AddRefType)( void); 37 typedef long (*ReleaseType)( void); 38 39 typedef long (CMyTest::*QueryInterfaceClassType)( long riid, void * * ppvObject); 40 41 void main() 42 { 43 CMyTest * pTest = new CMyTest; 44 //pTest->AddRef(); 45 46 int* pFirst = (int*)( *(int*)(pTest)); 47 48 cout<<"IMyMath virtual table call"<<endl; 49 for(int i=0; i<4; i++) 50 { 51 ((QueryInterfaceType)(*(pFirst+i)))(0, NULL); 52 cout<<int((QueryInterfaceType)(*(pFirst+i)))<<endl; 53 } 54 55 cout<<endl<<"--------------------------------------------------"<<endl<<endl; 56 57 cout<<"IStr virtual table call"<<endl; 58 for(int i=0; i<4; i++) 59 { 60 int* pFirst2 = (int*)( *((int*)(pTest)+1)); 61 ((QueryInterfaceType)(*(pFirst2+i)))(0, NULL); 62 cout<<int((QueryInterfaceType)(*(pFirst2+i)))<<endl; 63 } 64 }
后记:
之前提出一个问题,IUnknown的三个函数地址在两个虚函数表里都各有一份,但是地址值打印出来不一样,用VS command prompt打印class layout如下图,下面的IStr的虚函数表里多了个this -= 4,这里的4是IStr与实际对象指针也就是this指针的偏移量。
参考文章:
http://bbs.csdn.net/topics/390223914
http://blog.csdn.net/wxc1987821/article/details/5958325