Inside COM接口

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;
};




原文地址:https://www.cnblogs.com/javawebsoa/p/3069607.html