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