1.接口的作用
组件可以充应用程序中删除并可用另外一个组件代替,只要新的组件支持同样的接口。单个组件并不能起决定性作用,相反,用以连接组件的接口对应用程序亲戚到决定性作用。使用组件来构成应用程序最大优点在于可以复用应用程序的结构。
接口可以保护系统免受外界变化的影响、接口可以使得客户用相同的方式处理不同的组件。
2.COM接口的实现
class
IX
//First Interface
{
public
:
virtual
void
Fx1() = 0;
virtual
void
Fx2() = 0;
};
class
IY
//Second Interface
{
public
:
virtual
void
Fy1() = 0;
virtual
void
Fy2() = 0;
};
class
CA :
public
IX,
public
IY
{
public
:
//implement Interface IX
virtual
void
Fx1(){cout<<
"Fx1()"
<<endl;}
virtual
void
Fx2(){cout<<
"Fx2()"
<<endl;}
//implement Interface IY
virtual
void
Fy1(){cout<<
"Fy1()"
<<endl;}
virtual
void
Fy2(){cout<<
"Fy2()"
<<endl;}
};
IX和IY是用于实现接口的纯抽象基类。组件CA继承了IX和IY接口。抽象基类指定了起派生类应提供哪些函数。而派生类则具体实现这些函数,对纯虚类的继承称作接口继承。com接口必须继承至IUnknown接口。
在objbase.h中定义:#define interface struct 所以使用struc 就不必留有public更简洁。
总结:
- com接口在c++中是用纯抽象基类实现的
- 一个com组件可以提供多个接口
- 一个c++类可以使用多继承来实现一个了可以提供多个接口的组件
(1) __stdcall调用
__stdcall是Pascal程序的缺省调用方式,参数采用从右到左的压栈方式,被调函数自身在返回前清空堆栈。WIN32 Api都采用__stdcall调用方式,这样的宏定义说明了问题: #define WINAPI _stdcall 按C编译方式,__stdcall调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如_functionName@nnn。
(2) __cdecl调用
__cdecl是C/C++的缺省调用方式,参数采用从右到左的压栈方式,传送参数的内存栈由调用者维护。__cedcl约定的函数只能被C/C++调用,每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用__stdcall函数的大。 由于_cdecl调用方式的参数内存栈由调用者维护,所以变长参数的函数能(也只能)使用这种调用约定。由于Visual C++默认采用__cdecl 调用方式,所以VC中中调用DLL时,用户应使用__stdcall调用约定。按C编译方式,__cdecl调用约定仅在输出函数名前面加下划线,形如_functionName。
3.实现细节
可以用一个类实现几个不同的接口,还可以用单个的类来实现每一个接口
一个接口是一个函数集合、一个组件是一个接口集合,而一个系统是一系列组件的集合。
4.接口的背后
虚拟函数表
当定义一个抽象基类时,定义的实际上是一个内存块的结构。纯抽象基类所有实现都是一些具有相同的基本结构的内存块
interface
IX
{
virtual
void
__stdcall
Fx1() = 0;
virtual
void
__stdcall
Fx2() = 0;
virtual
void
__stdcall
Fx3() = 0;
virtual
void
__stdcall
Fx4() = 0;
};
定义一个纯抽象基类也就是定义了相应的内存结构。次内存在派生类中实现此抽象基类的时候才会分配。当派生类继承一个抽象基类时,将继承此内存结构:
一个抽象基类定义的内存结构分为两部分。上图右边是虚拟函数表(vtbl)其中包含一组指向虚拟函数实现的指针,vtbl中第一项为派生类中所实现Fx1函数的地址。左侧为一个指向vtbl的指针,指向抽象基类的指针则指向vtbl指针。
vtbl指针及实例数据
class
CA:
public
IX
{
public
:
//implement interface IX
virtual
void
__stdcall
Fx1(){cout<<
"CA::Fx1()"
<<endl;}
virtual
void
__stdcall
Fx2(){cout<<m_Fx2<<endl;}
virtual
void
__stdcall
Fx3(){cout<<m_Fx3<<endl;}
virtual
void
__stdcall
Fx4(){cout<<m_Fx4<<endl;}
//
CA(
double
d):m_Fx2(d*d),m_Fx3(d*d*d),m_Fx4(d*d*d*d){}
//interface data
double
m_Fx2;
double
m_Fx3;
double
m_Fx4;
};