接口
某些時候,讓不相關的類分享一組公有成員,以便產生相同的行為,是非常有用的。一個最基本的方法可能是通過一個公共的基類來定義它們,但這種方法太受局限,因為它要求這些類通過繼續而互相關聯,另外,它們也許還有著各自的基類,且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。當然了,任何從抽象類繼續而來的類也是抽象類,除非定義了之前的這些抽象函數。
更多內容請看C/C++應用實例專題,或
接口不提供多重繼續,與此相比,一個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());
}
在標號1與2中,為訪問底層的List或Queue,使用了一個指向接口的句柄c,由此,你可傳遞給ProcessCollection一個指向任意對象的句柄,只要它的類實現了這個接口,或者它是從實現了這個接口的類繼續而來的。
例6演示了一個包含只讀屬性X、只寫屬性Y、讀寫屬性Z的接口,對讀寫屬性來說,get與set聲明的順序並不重要。
例6:
public interface class IProperties
{
property int X { int get(); }
property String^ Y { void set(String^ value); }
property Object^ Z { Object^ get(); void set(Object^ value); }
};
一個接口的成員,可以為靜態數據成員、實例或靜態函數、靜態構造函數、實例或靜態屬性、實例或靜態事件、操作符函數、或任意的嵌套類型。
一般來說,我們會用for each語句來枚舉集合中的所有元素,要對集合中的每個元素逐個進行操作,可使用如下語法:
for each (表達式形式的類型標識符)
嵌入語句
表達式類型必須為一個"集合類型",假如要成為一個集合類型,這個類型必須實現接口System::Collections::IEnumerable,如例7中所定義。
例7:
public interface class IEnumerable
{
IEnumerator^ GetEnumerator();
};
正如大家所見,GetEnumerator返回一個指向IEnumerator的句柄,如例8中所定義。
例8:
public interface class IEnumerator
{
bool MoveNext();
void Reset();
property Object^ Current { Object^ get(); }
};
System::Array為一個集合類型,因為所有的CLI數組類型都繼續自System::Array,所以,任何數組類型表達式都可以作為for each語句中的表達式。在例9的標號1中,for each用於遍歷一個int數組,標號2中的處理過程也一樣,但直接使用了枚舉器。
例9:
using namespace System;
using namespace System::Collections;
int main()
{
array<int>^ ary = gcnew array<int>{10, 20, 30, 40};
/*1*/ for each (int i in ary)
{
Console::Write(" {0}", i);
}
Console::WriteLine();
/*2*/ IEnumerator^ ie = ary->GetEnumerator();
while (ie->MoveNext())
{
Console::Write(" {0}", static_cast<int>(ie->Current));
}
Console::WriteLine();
}
更多內容請看C/C++應用實例專題,或
泛型
就像函數能用一個或多個類型表示符來定義一樣,類型也可以這樣來定義。假如有這樣一種情況,某種類型建模了一個"數組",其可使用下標來訪問每個元素,這樣的類型往往被稱為"向量",實現一個向量之後,可以保存一組int、一組double、或一組用戶自定義類型的元素。然而,正是因為每種類型實現的代碼對類型中的元素來說,都是唯一的,因此,可使用泛型機制來定義一個向量類型,並創建特定類型的實例。例10就是這樣的一個例子。
例10:
generic <typename T>
public ref class Vector
{
int length;
/*1*/ array<T>^ vector;
public:
property int Length
{
int get() { return length; }
private:
void set(int value) { length = value; }
}
/*2*/ property T default[int]
{
T get(int index) { return vector[index]; }
void set(int index, T value) { vector[index] = value; }
}
Vector(int vectorLength, T initValue)
{
Length = vectorLength;
vector = gcnew array<T>(Length);
for (int i = 0; i < Length; ++i)
{
/*3*/ this[i] = initValue;
}
/*4*/ //for each (T element in vector)
//{
// element = initValue;
/