接口
某些時候,讓不相關的類分享一組公有成員,以便產生相同的行為,是非常有用的。一個最基本的方法可能是通過一個公共的基類來定義它們,但這種方法太受局限,因為它要求這些類通過繼承而互相關聯,另外,它們也許還有著各自的基類,且CLI類型只支持單一類繼承。
C++/CLI提供了一種方法,可利用多個類實現一組通用的功能,這就是我們通稱的"接口",而一個接口則是一組成員函數的聲明。要注意,這些函數只是聲明,沒有定義,也就是說,一個接口定義了一個由抽象函數組成的類型--這些函數實際上是純虛函數,且在適當的時候,這些函數由客戶類來實現。一個接口可允許不相關的類用同一名稱和類型,實現同一功能,而無須要求這些類分享公共基類。在例1中演示了怎樣定義一個接口。
例1:
using namespace System;
public interface class ICollection
{
void Put(Object^ o); //隱式public abstract
Object^ Get(); //隱式public abstract
};
一個接口的定義看上去非常像一個類,除了用interface取代了ref或value,所有的函數都沒有函數體,且均隱式為public和abstract。按照通常的約定,一個接口名帶有起始字母I,後再接一個大寫字母。(接口類與接口結構是等價的。)與類相似,一個接口也能有public或private訪問可見性。
一個接口能有一個或多個"基接口",在這種情況下,它將繼承這些接口中的所有抽象函數,例如,在例2中,接口I2顯式繼承自I1,而I3顯式繼承自I1與I2,並通過I2隱式繼承自I1。
例2:
interface class I1 { /* ... */ };
interface class I2 : I1 { /* ... */ };
interface class I3 : I1, I2 { /* ... */ };
一個類可像從基類繼承時那樣,來實現一個接口,見例3。
例3:
public ref class List : ICollection
{
public:
void Put(Object^ o)
{
// ...
}
Object^ Get()
{
// ...
}
// ...
};
一個類能實現一個以上的接口,在這種情況下,必須使用逗號來分隔接口列表,順序倒不是很重要。當然,一個類在實現一個或多個接口時,也能顯式地帶有一個基類,在這種情況下,基類通常(但不是必須)寫在最前面。
如果一個類實現了一個接口,但沒有定義接口中所有的函數,這個類就必須聲明為abstract。當然了,任何從抽象類繼承而來的類也是抽象類,除非定義了之前的這些抽象函數。
接口不提供多重繼承,與此相比,一個CLI類也只能有一個基類,然而,接口卻提供某種與多重類繼承相似的功能,但概念與之完全不同,例如,一個類不能從接口中繼承函數定義;接口繼承體系是獨立於類繼承體系的--實現同一接口的類也許會、但也許不會通過類繼承體系相互關聯。
例4演示了一個類:Queue,其與List無關聯(但除了這個外,兩者都是從Object繼承而來的),兩者都實現了同一接口。
例4:
public ref class Queue : ICollection
{
public:
void Put(Object^ o)
{
// ...
}
Object^ Get()
{
// ...
}
// ...
};
現在,可用它來編寫處理參數為List或Queue的函數了,如例5。
例5:
ref class Item { /* ... */ };
void ProcessCollection(ICollection^ c);
int main()
{
List^ myList = gcnew List;
Queue^ myQueue = gcnew Queue;
ProcessCollection(myList);
ProcessCollection(myQueue);
}
void ProcessCollection(ICollection^ c)
{
Item^ x = gcnew Item();
/*1*/ c->Put(x);
/*2*/ x = static_cast<Item^>(c->Get());
}