interface接口实例
C++標準中是沒有接口interface的,但是我們在MFC的程序中經常可以看到接口的影子,我就自己查閱的資料和理解,做一個總結把。
首先,在objBase.h里面,有interface的定義
#define __STRUCT__ struct
#define interface __STRUCT__
這樣我們就能知道,interface只不過是struct的重命名
?
但是為什么要把struct定義為interface呢,這其中大有淵源,接著往下看:
| 在C++中struct與Class可以說是沒有區別,但是又可以說是很大區別。 因為C++要向下兼容C。所以C具有的東西,那么在C++里也具有。所以談struct應該是從C談起 struct在C中:struct是一種自定義的數據類型。既然是一種數據類型那么就肯定不能定義函數。C是面向過程的,面向對象的東西它自然也不具有。 struct在C++中;C++是面向對象的。根據對象的思想,struct應該是等價于CLass(C是沒有Class這個東西的)。這里我們又引出了一個很大的話題,面向過程和面向對象的區別。這里我們只選其中很小的一部分來說。 面向過程認為,數據和數據的操作是分開的。(當然面向過程也可以刻意的實現把數據和數據的操作集合到一起) 面向對象認為,數據和數據的操作是一個整體,不應該分開的。 這樣面向對象就和面向過程有了很大的沖突。這個沖突其實引發了struct的C和C++的差異性。 C++中的struct是可以包含函數的,可以擁有構造函數,析構函數同樣擁有繼承等能力。這個時候。。很多人就會疑惑?那struct和CLASS不就一樣了嗎? 對struct和class的確差別不大。從使用上差別不大。使用上的差別唯一的就是默認訪問類型不一樣struct默認公有,class默認私有。(改了- -!手誤。。。) 肯定就有朋友會問,平常我們都不會依賴于默認啦,不同的編譯器可能默認方式都不一樣的。照這個思維不就是完全沒區別了。對我說的很大區別,其實針對的是思想上并非使用上。 第一:struct是繼承于C但又要適合于面向對象。所以struct是丟也不是,留下又多余的尷尬局面。 第二:C++中的struct已經被擴展,已經不再是C時代的struct 那么最大的思想差別是啥呢?我也說不清楚。思想的東西我覺得,是要靠感悟的,可能從我嘴來說出來的思想,在你眼里可能是狗屁不通,甚至是一堆臭雞蛋。所以下面我說一個我認為的C++中struct思想。 在面向對象橫行的時代。相信大家都對幾個面向對象詞語有很深的認識,對象,類,接口,繼承等等。 但是大家留心會發現,C++里面沒有“接口”這個東西。說起接口這個東西,就不能不說java了,在JAVA里是不允許多繼承的,但是能實現繼承多個接口。但是C++卻是允許實現多繼承,那么如果C++里出現接口這個東西,最后的結果也會變成多余。 所以我覺得“接口”其實已經在面向對象里形成了一種文化。但是作為編程界翹楚的C++卻沒有“接口”這樣東西。人的思維是強大的,例如MS的做法就是將struct當成了接口來使用。 "我覺得C++中的struct和class區別不是在于使用,不在于語法糖,而是在于思想,在于構架約定等方面。" |
從以上我們可以知道,使用struct是為了和Java的面向對象編程思想保持一致,也就是說其實用abstract class也是一樣的效果,但是interface看起來更像面向對象一些。
?
?
?
接口使用方法實例:
?
| #include <iostream> using namespace std; ? #include <objbase.h>?? // Define interface. ? /* ??? 輸出一個字符串 */ void trace(const char* pMsg) { ??? cout << pMsg << endl; } ? /* ??? IX和IY為普通接口(非COM接口),其實它們是抽象基類 */ interface IX { ??? virtual void __stdcall Fx1() = 0 ; ??? virtual void __stdcall Fx2() = 0 ; }; ? interface IY { ??? virtual void __stdcall Fy1() = 0 ; ??? virtual void __stdcall Fy2() = 0 ; }; ? /* ??? 接口的實現,這里用了多重繼承 */ class CA : public IX, ?????? ?? public IY { public: ? ??? // Implement interface IX. ??? virtual void __stdcall Fx1() {cout << "CA::Fx1" << endl;} ??? virtual void __stdcall Fx2() {cout << "CA::Fx2" << endl;} ? ??? // Implement interface IY. ??? virtual void __stdcall Fy1() {cout << "CA::Fy1" << endl;} ??? virtual void __stdcall Fy2() {cout << "CA::Fy2" << endl;} ? }; ? /* ??? 客戶(在這里是一個主函數) */ int main() { ??? trace("Client: Create an instance of the component."); ??? CA* pA = new CA; ? ??? // Get an IX pointer. ??? IX* pIX = pA; ? ??? trace("Client: Use the IX interface."); ??? pIX->Fx1(); ??? pIX->Fx2(); ? ??? // Get an IY pointer. ??? IY* pIY = pA; ? ??? trace("Client: Use the IY interface."); ??? pIY->Fy1(); ??? pIY->Fy2(); ? ??? trace("Client: Delete the component."); ??? delete pA; ? ??? return 0; } |
?
雖然C++可以直接操作和使用實例數據,但COM組件絕不會訪問任何實例數據,在COM中,對一個組件的訪問只能通過函數完成,而絕不能直接通過變量.這一點同我們對COM組件的定義是相符的.純抽象基類只有虛擬函數,而沒有任何實例數據.
?
對于COM來說,接口是一個包含一個函數指針數組的內存結構。
接口是由沒有實現細節的虛線基類實現的。
?
編碼約定
在所有的前面都加有一個字母I, 如:IX表示的“接口X”。
而在類名稱的前面所加的前綴則為C, 如CA表示“類A”。
Microsoft Win32軟件開發工具(SDK)中OBJBASE.H頭文件的定義:#define interface struct
使用struct的原因在于struct的成員將自動具有我公有的屬性,因此不需要另外定義中加上public關鍵字。
?
標準調用
Microsoft平臺上COM接口所提供的所有函數使用的均是標準的調用約定.參數數目可變的函數使用的則是C調用約定.一般人們希望接口的實現使用這些約定.但要說明的是這并不是COM的絕對需要.
在WINDEF.H中pascal的定義如下:
#define pascal __stdcall
如果讀者認為將pascal這個詞放在代碼中會讓人莫名其妙,那么可以使用OBJBASE.H中所定義的如下宏:
#define STDMEFHODCALLTYPE __stdcall
?
類并非組件
用C++開發組件時不一定非用類不可。組件也可以用C來實現。一個組也可以由多個類來實現。
?
接口并非總是繼承的
COM沒有要求實現某個接口的類必須從那個接口繼承,這是客戶并不需要了解COM組件的繼承關系。對接口的繼承只不過是一種實現細節而已。
?
多重接口及多重繼承
一個接口是一個函數集合,一個組件則是一個接口集,而一個系統則是一系列組件的集合。
?
接口不變性
一旦分布了一個接口,那么它將永遠保持不變。當對組件進行升級時,一般不會修改已有的接口,面是加入一些新接口。
命名沖突
對于一個支持多個接口的組件,接口函數的名字出現沖突是經常會遇到的。這些種情況下,改變某個發生沖突的函數名稱即可。COM對此不關心。COM接口是一個二進制標準,客戶同接口的連接并不是通過其名稱或其成員函數的名稱完成的,而是通過它在表示它的內存塊的位完成的。
解決命名沖突的另外一種 方法是不使用多重繼承。
實現組件的類并不需要繼承每一個接口,而可以使用指向實現某些接口的類的指針。
接口名稱之間出現沖突的情況也是可能的。如果在接口和函數名稱的前面加上公司名稱或產品名稱可以減少此種 可能性。
多態
多態指的是可以按同一種方式來處理不同的對象。若兩個不同的組件支持同一接口,那么客戶將可以使用相同的代碼一處理其中的任何一個組件。就是說,客戶可以按照相同的方式來處理不同的組件。
虛擬函數表
當定義一個純抽象類時,所定義的實際上是一個內存塊的結構.純抽象類所有實現都是一些具有相同的基本結構的內存.
?
如下定義一個抽象基類。內存結構如圖2-4所示:
interface IX
{
??? virtual void __stdcall Fx1() = 0 ;
??? virtual void __stdcall Fx2() = 0 ;
??? virtual void __stdcall Fx3() = 0 ;
??? virtual void __stdcall Fx4() = 0 ;
};
?
定義一個抽象基類也就是定義了相應的內存結構。但些內存只在派生在中實現些抽象基類時才會被分配。當派生類繼承一個抽象基類時,它將繼承此內存結構。
?
似乎是一個的偶然的巧合,COM接口的內存結構同C++編譯器為抽象基類所生成的內存結構是相同的.因此可以使用抽象基類來定義COM接口.
對于一個COM接口還有其他一些需求.例如,所有的COM接口都必須繼承一個名為Iunknown的接口.
?
Vtbl指針及實例數據
Vtbl指針在由抽象基類函數指針到函數的過程中增加了一個額外的級別。這帶了很大的靈活性。
實現抽象基類的類可能會將特定于實例的信息同vtbl一塊保存。如下IX的實現類CA:
class CA : public IX
{
public:
??? 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)
??? {
?
??? }
?
??? double m_Fx2;
??? double m_Fx3;
??? double m_Fx4;
};
多重實例
在C++中同一個類的不同實例可以共享同一個vtbl。如下:
int main()
{
??? CA* pA1 = new CA(4.3);
??? CA* pA2 = new CA(5.1);
??? ...
??? ...
}
?
雖然COM組件可以使用vtbl指針來共享vtbl,但這一點并不是必需的。COM組件的每個實例中已有一個不同的vtbl
?
不同的類,相同的vtbl
?
接口的真正威力在于繼承此接口的所有類均可以被客戶按同一方式進行處理。
例子:
?
class CB : public IX
{
public:
??? // implementatio inerface IX.
??? virtual void __stdcall Fx1(){cout << "CB::Fx1" << endl;}
??? virtual void __stdcall Fx2(){cout << "CB::Fx2" << endl;}
??? virtual void __stdcall Fx3(){cout << "CB::Fx3" << endl;}
??? virtual void __stdcall Fx4(){cout << "CB::Fx4" << endl;}
?
};
?
void foo(IX* pIX)
{
??? pIX->Fx1();
??? pIX->Fx2();
}
?
int main()
{
??? CA* pA = new CA(4.3);
?
??? CB* pB = new CB;
?
??? IX* pIX = pA;
??? foo(pIX);
?
??? pIX = pB;
??? foo(pIX);
}
從上例中,我們將CA和CB都當成是IX接口來作用。這也是多態的一個例子。
從上圖可以看出,CA 和 CB 分別具有不同的實例數據、vtbl以及實現。但是因其具有相同的而可以按相同的方式來訪問。?
總結
以上是生活随笔為你收集整理的interface接口实例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c++继承:共有、私有、保护继承
- 下一篇: 1_1 FactoryMode 工厂模式