9.9.9 通用类(1)
C++(www.cppentry.com)/CLI给我们提供了定义通用类的功能,具体类将在运行时通过实例化通用类产生。我们可以定义通用数值类、通用引用类、通用接口类和通用委托。定义通用类的方法是使用一个或多个形参,这与第6章定义通用函数的方法相似。
例如,我们可以像下面这样,定义Ex9_14示例中Stack类的通用版本:
- // Stack.h for Ex9_21
- // A generic pushdown stack
-
- generic<typename T> ref class Stack
- {
- private:
- // Defines items to store in the stack
- ref struct Item
- {
- T Obj; // Handle for the object in this item
- Item^ Next; // Handle for next item in the stack or nullptr
-
- // Constructor
- Item(T obj, Item^ next): Obj(obj), Next(next){}
- };
-
- Item^ Top; // Handle for item that is at the top
-
- public:
- // Push an object on to the stack
- void Push(T obj)
- {
- Top = gcnew Item(obj, Top); // Create new item and make it the top
- }
-
- // Pop an object off the stack
- T Pop()
- {
- if(Top == nullptr) // If the stack is empty
- return T(); // return null equivalent
- T obj = Top->Obj; // Get object from item
- TopTop = Top->Next; // Make next item the top
- return obj;
- }
- };
该类的通用版本现在有个类型形参T。注意,我们在指定该形参时可以使用class关键字代替typename关键字,二者在此处的上下文中没有区别。当使用通用类类型时,类型实参将代替形参T,T是通过类定义由类型实参代替的,因此与原来版本相比通用类类型的主要优点是更加安全,但不会丧失任何灵活性。原来类的Push()成员可接受任何句柄,因此我们可以将MyClass^类型、String^类型或任何句柄类型的对象混合压入相同的堆栈,但通用类实例的Push()函数只能接受实参类型的对象以及以实参为基类的类型的对象。
我们看一下Pop()函数的实现。如果栈顶项为空,原来的版本将返回nullptr,但我们在返回类型为形参的情况下不能返回nullptr,因为类型实参可能是数值类型。解决方法是返回T(),以调用类型T中无参数的构造函数,这样做的结果等价于为数值类型返回0,为句柄类型返回nullptr。
注意:
我们可以用第6章为通用函数指定约束的相同方式,使用where关键字来指定对通用类类型形参的约束。
如下所示,我们可以用Stack<>通用类型创建一个存储Box对象句柄的堆栈:
- Stack<Box^>^ stack = gcnew Stack<Box^>;
类型实参Box^处于尖括号之间,该语句将在CLR堆上创建一个Stack<Box^>对象。该对象允许将Box^类型以及任何以Box为直接或间接基类的类型的句柄压入堆栈。我们可以用Ex9_14的修订版来试一试。
试一试:使用通用类类型
创建一个新的名为Ex9_21的CLR控制台程序,然后将Container.h、Box.h和GlassBox.h这3个头文件从Ex9_14复制到本项目的目录中。在Solution Explorer选项卡中右击Header Files,从弹出的上下文菜单中选择Add | Existing Item,将这些头文件添加到项目中。然后,我们可以给该项目添加一个新的头文件Stack.h,并输入前面介绍的通用Stack类定义。不要忘记在该文件开始处添加#pragma once指令。
- // Ex9_21.cpp : main project file.
-
- // Using a nested class to define a stack
-
- #include "stdafx.h"
- #include "Box.h" // For Box and Container
- #include "GlassBox.h" // For GlassBox (and Box and Container)
- #include "Stack.h" // For the generic stack class
-
- using namespace System;
-
- int main(array<System::String ^> ^args)
- {
- array<Box^>^ boxes = { gcnew Box(2.0, 3.0, 4.0),
- gcnew GlassBox(2.0, 3.0, 4.0),
- gcnew Box(4.0, 5.0, 6.0),
- gcnew GlassBox(4.0, 5.0, 6.0)
- };
- Console::WriteLine(L"The array of boxes have the following volumes:");
- for each(Box^ box in boxes)
- box->ShowVolume(); // Output the volume of a box
- Console::WriteLine(L"\nNow pushing the boxes on the stack...");
-
- Stack<Box^>^ stack = gcnew Stack<Box^>; // Create the stack
- for each(Box^ box in boxes)
- stack->Push(box);
-
- Console::WriteLine(
- L"Popping the boxes off the stack presents them in reverse order:");
- Box^ item;
- while((item = stack->Pop()) != nullptr)
- safe_cast<Container^>(item)->ShowVolume();
-
- // Try the generic Stack type storing integers
- Stack<int>^ numbers = gcnew Stack<int>; // Create the stack
- Console::WriteLine(L"\nNow pushing integers on to the stack:");
- for(int i = 2 ; i<=12 ; i += 2)
- {
- Console::Write(L"{0,5}",i);
- numbers->Push(i);
- }
- int number;
- Console::WriteLine(L"\n\nPopping integers off the stack produces:");
- while((number = numbers->Pop()) != 0)
- Console::Write(L"{0,5}",number);
-
- Console::WriteLine();
- return 0;
- }