如同泛型函数一样,泛型的定义一般由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++要求每种类型都有一个默认的构造函数--甚至对不是类的类型也是这样,一般表示为类型名后紧跟空的圆括号。
程序在构造函数没有参数的情况下(标号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)); } |
| 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 |
| 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; |
| generic <typename T> where T : ValueType public ref class Vector, IComparable { ... }; |

