亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

實例解析C++/CLI中的接口與泛型

2019-11-17 05:03:06
字體:
來源:轉載
供稿:網友
接口

  某些時候,讓不相關的類分享一組公有成員,以便產生相同的行為,是非常有用的。一個最基本的方法可能是通過一個公共的基類來定義它們,但這種方法太受局限,因為它要求這些類通過繼續而互相關聯,另外,它們也許還有著各自的基類,且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;
   //}
  }
  Vector(int vectorLength)
  {
   Length = vectorLength;
   vector = gcnew array<T>(Length);
   for (int i = 0; i < Length; ++i)
   {
    /*5*/ this[i] = T();
   }
  }
  Vector()
  {
   Length = 0;
   /*6*/ vector = gcnew array<T>(0);
  }
  ~Vector() //多余的
  {
   /*7*/ vector = nullptr;
  }
  virtual String^ ToString() override
  {
   String^ s = "[";
   int i;
   for (i = 0; i < Length - 1; ++i)
   {
    /*8*/ s = String::Concat(s, this[i], ":");
   }
   /*9*/ s = String::Concat(s, this[i], "]");
   return s;
  }
  virtual bool Equals(Object^ obj) override
  {
   if (obj == nullptr)
   {
    return false;
   }
   if (this == obj) //在測試自身嗎?
   {
    return true;
   }
   /*10*/ if (GetType() == obj->GetType())
   {
    Vector<T>^ v = static_cast<Vector^>(obj);
    if (Length != v->Length) //是否向量有不同的長度?
    {
     return false;
    }
    for (int i = 0; i < Length; ++i)
    {
     /*11*/ //if (this[i] != v[i])
     if (this[i]->Equals(v[i]) == false)
     {
      return false;
     }
    }
    return true;
   }
   return false;
  }
  /*12*/ virtual int GetHashCode() override
  {
   return 0;
  }
};
  如同泛型函數一樣,泛型的定義一般由generic <typename t1, ..., typename tn>打頭,意思是通過tn引入了類型參數t1,這些參數的作用范圍,可從類型定義的結束直到它們所依附的對象。在本例中,只有一個類型參數T。

  在標號1中可見,一個Vector是作為一個類型為T的元素數組存儲的。

  在標號2中,定義了一個默認的索引屬性,以便可用一個int下標值來訪問一個Vector。當然了,在運行時,我們可以存取類型為T的元素,而不用管它實際上是什么類型。

  與其直接訪問私有成員,倒不如通過公有屬性來進行訪問,比如說,用Length來取代length;使用下標來訪問當前的Vector(標號3)時,我們使用了this[i]。在此,很有可能會想到使用for each循環來取代一般的老式for循環,如標號4,但是,在此使用for循環是行不通的。在for each循環中,通過命名在循環結構中的局部變量,可為每個元素都產生一個副本,也就是說,變量已經不是原來的元素了,因此,修改它的值不會改變元素的值。

  在標號5中,需要把每個元素設為默認值,幸運的是,標準C++要求每種類型都有一個默認的構造函數--甚至對不是類的類型也是這樣,一般表示為類型名后緊跟空的圓括號。 更多文章 更多內容請看C/C++應用實例專題,或
  程序在構造函數沒有參數的情況下(標號6),分配了一個零元素數組。(注重,指向零元素數組的句柄,不等同于包含nullptr的句柄。)這樣做的目的是為了讓成員函數可以正常工作,甚至對空Vector也是如此。


  為了完整起見,還定義了一個析構函數,以把句柄設為nullptr,目的是為了顯式地告之垃圾回收器,我們對這個對象及與之關聯內存的工作已經完成。

  使用在標號8及9中的this[i]還是真有點讓人糊涂,那它真正的目的是什么呢?不像等價的模板類,泛型類Vector是在類型T未知的情況下編譯為一個程序集,假如編譯器不知道T的類型,它也不知道this[i]的類型,因此,它該怎樣生成代碼,才能把這個表達式轉換為Concat所期望的類型呢?其實它不必知道,Concat的其中一個重載版本會要求它的第二個參數為Object^類型,且因為this[i]有類型T,無論類型T是否處在運行時,它都是從System::Object繼續來的,因此,它與參數類型相兼容。更進一步來說,因為System::Object有一個名為ToString的虛函數,對此虛函數的調用將產生一個String^,假定類型T已覆蓋了這個函數,那么就可返回正確的字符串了。

  函數Equals非常簡單明了,但有一件事需要指出,請看標號11,從直覺上來說,我們一般會在此寫成!=操作符,但是,這卻通不過編譯。請記住,類Vector是在對類型T一無所知的情況下編譯的,除了它是繼續自System::Object,同樣地,唯一它答應對T調用的成員函數是那些由System::Object提供的函數,而這些類型未定義!=操作符,幸運的是,它提供了Equals函數,因此可使用它來達到同樣的目的;接著,類型T覆蓋了這個函數以執行對兩個T的相等性判別,例11是主程序。

  例11:

int main()
{
 /*1*/ Vector<int>^ iv1 = gcnew Vector<int>(4);
 /*2*/ Console::WriteLine("iv1: {0}", iv1);
 /*3*/ Vector<int>^ iv2 = gcnew Vector<int>(7, 2);
 Console::WriteLine("iv2: {0}", iv2);
 iv2[1] = 55;
 iv2[3] -= 17;
 iv2[5] *= 3;
 Console::WriteLine("iv2: {0}", iv2);
 /*4*/ Vector<String^>^ sv1 = gcnew Vector<String^>(3);
 Console::WriteLine("sv1: {0}", sv1);
 /*5*/ Vector<String^>^ sv2 = gcnew Vector<String^>(5, "X");
 Console::WriteLine("sv2: {0}", sv2);
 sv2[1] = "AB";
 sv2[3] = String::Concat(sv2[4], "ZZ");
 Console::WriteLine("sv2: {0}", sv2);
 /*6*/ Vector<DateTime>^ dv1 = gcnew Vector<DateTime>(2);
 Console::WriteLine("dv1: {0}", dv1);
 /*7*/ Vector<DateTime>^ dv2 = gcnew Vector<DateTime>(3, DateTime::Now);
 Console::WriteLine("dv2: {0}", dv2);
 for (int i = 0; i < dv2->Length; ++i)
 {
  Thread::Sleep(1100);
  dv2[i] = DateTime::Now;
 }
 Console::WriteLine("dv2: {0}", dv2);
 /*8*/ Vector<Vector<int>^>^ viv = gcnew Vector<Vector<int>^>(3);
 Console::WriteLine("viv: {0}", viv);
 viv[0] = gcnew Vector<int>(2, 1);
 viv[1] = gcnew Vector<int>(4, 2);
 viv[2] = gcnew Vector<int>(3, 5);
 Console::WriteLine("viv: {0}", viv);
 /*9*/ Vector<int>^ iv3 = gcnew Vector<int>(4,3);
 Vector<int>^ iv4 = gcnew Vector<int>(4,3);
 Vector<int>^ iv5 = gcnew Vector<int>(4,2);
 Vector<int>^ iv6 = gcnew Vector<int>(5,6);
 Console::WriteLine("iv3->Equals(iv4) is {0}", iv3->Equals(iv4));
 Console::WriteLine("iv3->Equals(iv5) is {0}", iv3->Equals(iv5));
 Console::WriteLine("iv3->Equals(iv6) is {0}", iv3->Equals(iv6));
}
  為創建Vector的一個特定類型,可在尖括號中作為類型參數指定一個類型,如標號1、3所示。記住,int是值類System::Int32的同義詞。(假如泛型有多個類型參數,需要以逗號把它們分隔開。)

  在標號4、5中,定義了一個String^類型的Vector--一個引用類型;在標號6、7中,定義了一個DateTime類型的Vector--一個值類型;在標號8中,定義了一個Vector,而其的元素則為int類型的Vector;最后,用不同的length及Vector int值測試了Equals函數,插1是程序的輸出:

  插1:例11的輸出


iv1: [0:0:0:0]
iv2: [2:2:2:2:2:2:2]
iv2: [2:55:2:-15:2:6:2]
sv1: [::]
sv2: [X:X:X:X:X]
sv2: [X:AB:X:XZZ:X]
dv1: [1/1/0001 12:00:00 AM:1/1/0001 12:00:00 AM]
dv2: [4/9/2005 3:30:40 PM:4/9/2005 3:30:40 PM:4/9/2005 3:30:40 PM]
dv2: [4/9/2005 3:30:41 PM:4/9/2005 3:30:42 PM:4/9/2005 3:30:43 PM]
viv: [::]
viv: [[1:1]:[2:2:2:2]:[5:5:5]]
iv3->Equals(iv4) is True
iv3->Equals(iv5) is False
iv3->Equals(iv6) is False
  實際上,不但可以定義泛型類,還可以定義泛型接口與代理。

  在前面,也指出了調用泛型成員函數時的一些限制,默認情況下,一個類型參數被限定為從System::Object繼續來的任意類型,也就是任意CLI類型。對泛型或函數中的類型參數,假如使用where子句,還可以施加一個或多個限定條件,見例12。

  例12:

generic <typename T>
where T : ValueType
public ref class Vector
{ ... };
value class C {};

/*1*/ Vector<int>^ iv;
/*2*/ Vector<String^>^ sv; //錯誤
/*3*/ Vector<DateTime>^ dv;
/*4*/ Vector<Vector<int>^>^ viv; //錯誤
/*5*/ Vector<C>^ cv;
  在本例中,編譯器將答應任意有著System::ValueType類型的類型參數,或從它繼續來的類型被傳遞給泛型。假設此時又想定義某種特定的類型,如標號2和3所示,就會出錯,因為String與Vector<int>不是值類型。同樣地,假如換成例13中的限定條件,那么標號5就會被拒絕,因為值類型C沒有實現接口System::IComparable,而System::Int32和System::DateTime卻實現了。

  例13:

generic <typename T>
where T : ValueType
public ref class Vector, IComparable
{ ... };
  一旦編譯器知道T可以被限定為比System::Object更明確的類型,就會答應調用這些類型中的成員函數,而這些類型則可包含一個基類型或任意順序、任意數目的接口類型。 更多文章 更多內容請看C/C++應用實例專題,或

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲伊人久久综合| 国产精品青青在线观看爽香蕉| 欧美自拍视频在线观看| 国产精品美女主播| 国产v综合ⅴ日韩v欧美大片| 岛国av一区二区在线在线观看| 国产不卡一区二区在线播放| 91国产在线精品| 国产一区欧美二区三区| 国产欧美精品va在线观看| 中文字幕国产亚洲| 国产亚洲成精品久久| 亚洲欧洲av一区二区| 欧美亚洲视频在线看网址| 成人性生交xxxxx网站| 日韩免费观看网站| 91亚洲国产成人精品性色| 久久精品影视伊人网| 国产精品美女久久久免费| 欧美日韩激情视频8区| 欧美成人小视频| 国产91精品黑色丝袜高跟鞋| 永久免费精品影视网站| 91av视频导航| 久久99视频精品| 亚洲激情视频在线播放| 欧美影院久久久| 亚洲free性xxxx护士白浆| 一区二区成人精品| 精品国产鲁一鲁一区二区张丽| 欧美体内谢she精2性欧美| 国产精品国内视频| 亚洲精品720p| 欧美激情性做爰免费视频| 91在线免费看网站| 在线精品视频视频中文字幕| 欧美一性一乱一交一视频| 亚洲欧美日韩精品久久| 日韩av在线免费看| 91免费视频网站| 日韩av最新在线| 亚洲最大的成人网| www国产精品视频| 欧美国产高跟鞋裸体秀xxxhd| 成人国产精品免费视频| 福利微拍一区二区| 亚洲色图色老头| 日韩av中文字幕在线播放| 日韩精品极品视频| 久久综合色影院| 一区二区三区国产在线观看| 68精品久久久久久欧美| 欧美成人免费一级人片100| 亚洲v日韩v综合v精品v| 日韩高清av在线| 日本91av在线播放| 欧美国产第二页| 国产不卡一区二区在线播放| 日韩欧美在线免费| 91精品国产色综合| 国产成人av在线| 91国自产精品中文字幕亚洲| 久久亚洲成人精品| 2019国产精品自在线拍国产不卡| 在线免费观看羞羞视频一区二区| 日韩av高清不卡| 日本欧美中文字幕| 亚洲欧美精品伊人久久| 2019亚洲男人天堂| 亚洲欧美日韩另类| 在线日韩中文字幕| 国产日韩在线播放| 国产精品久久久久9999| 亚洲综合最新在线| 国产一区二区在线免费视频| 国产精品女人网站| 亚洲精品国产美女| 国产精品国内视频| 久久欧美在线电影| 久久亚洲精品中文字幕冲田杏梨| 久久男人的天堂| 亚洲国产精彩中文乱码av在线播放| 欧美色欧美亚洲高清在线视频| 久久免费成人精品视频| 亚洲аv电影天堂网| 亚洲最新av在线网站| 国产精品v片在线观看不卡| 欧美视频在线视频| 国产xxx69麻豆国语对白| 亚洲免费av片| 成人免费网站在线看| 日韩亚洲一区二区| 欧美成人激情视频免费观看| 成人精品一区二区三区电影黑人| 久久久91精品国产一区不卡| 久久999免费视频| 亚洲精品久久久久| 91香蕉嫩草神马影院在线观看| 欧美精品成人91久久久久久久| 91综合免费在线| www.亚洲一区| 中国日韩欧美久久久久久久久| 精品人伦一区二区三区蜜桃网站| 亚洲剧情一区二区| 久久久久国产一区二区三区| 国产精品h片在线播放| 成人激情综合网| 中文字幕欧美在线| 久久免费视频这里只有精品| 成人一区二区电影| 欧美疯狂做受xxxx高潮| 国产欧美日韩精品丝袜高跟鞋| 亚洲欧美福利视频| 91av视频在线观看| 欧美精品中文字幕一区| 久久精品欧美视频| 亚洲欧洲激情在线| 国内精品国产三级国产在线专| 欧美福利视频网站| 日韩视频欧美视频| 国产成人一区二区三区电影| 国产精品自拍偷拍| 久久免费国产视频| 亚洲电影免费观看高清完整版| 91日韩在线视频| 亚洲午夜久久久久久久| 日韩有码在线电影| 久久精品中文字幕电影| 亚洲成av人乱码色午夜| 大伊人狠狠躁夜夜躁av一区| 欧美成人免费va影院高清| 51视频国产精品一区二区| 国产成人鲁鲁免费视频a| 欧美成人国产va精品日本一级| 国产精品色视频| 欧美日韩在线视频一区二区| 国产精品日韩av| 日韩视频在线观看免费| 米奇精品一区二区三区在线观看| 午夜精品久久久久久99热软件| 欧美电影免费在线观看| 亚洲欧美在线播放| 亚洲精品国产suv| 色婷婷综合成人| 欧美又大粗又爽又黄大片视频| 欧美国产日韩一区二区| 97激碰免费视频| 91精品视频一区| 久久人人爽人人爽爽久久| 欧美高清在线播放| 91免费精品视频| 日韩在线免费视频观看| 久久网福利资源网站| 91在线观看免费| 欧美xxxx14xxxxx性爽| 黄色一区二区在线| 精品视频—区二区三区免费| 国产a级全部精品| 在线看日韩欧美| 欧洲亚洲在线视频| 国产精品www| 国产日韩视频在线观看| 国产日韩av在线播放| 日韩视频第一页|