e股脑电脑教程网
  • 首 页
  • 操作系统
  • 应用软件
  • 下载工具
  • 影音视频
  • 办公软件
  • 媒体制作
  • 网站建设
  • 平面设计
  • 数据库
  • 程序开发
  • 视频教程
编辑推荐: | 文章搜索:
您现在的位置: e股脑 >> 程序开发 >> ASP.net教程 >> Visual Studio 2005体验泛型编程 >> 教程正文
 
教程搜索
 
 
相关教程
  • C++/CLI实战——HELLO
  • VC编写简单的序列号(SN)填写器
  • Visual Studio 2005体验泛型编程
  • C++/CLI基本数据类型探索
  • 用托管C++编写Windows服务
  • Visual C++ 2005图像编程之属性设置栏
  • Visual C++ 2005图像编程之工具栏
  • Visual C++ 2005图像编程之预备知识
  • 把其他C/C++编译器集成到VC2005中
  • VC.NET实现清爽便利的Windows游戏窗口
  • C++:最强大的.NET语言之可访问性
  • C++:最强大的.NET语言之装箱
  • C++:最强大的.NET语言之内存与资源
  • C++:最强大的.NET语言之对象构造
  • Visual C++2005中开发自定义绘图控件
  • C++/CLI程序进程之间的通讯
 
 

图文教程


  • Windows抢了谁的饭碗 非主流操作系统To

  • 地球还是火星 平常心看“非主流”

  • 综合运用Office 2007批量制作奖状

  • 没有系统盘如何才能修复受损系统?

  • 巧妙运用Excel中边界的附加功能!
 
 
赞 助 商
 
 
Visual Studio 2005体验泛型编程
  • 来源:e股脑
  • 点击次数:
  • 更新时间:2007-8-9
s->pop());

// a String^ reference type argument...

tStack<String^> ^ss = gcnew tStack<String^>( 10 );

ss->push( "Pooh" ); ss->push( "Piglet" );

ss->push( "Rabbit" ); ss->push( "Eeyore" );

elem_cnt = ss->size();


for ( int ix = 0; ix < elem_cnt; ++ix )

Console::WriteLine( "({0}) {1}", ix+1, ss->pop());

}

Figure 4 Instantiating the Generic Stack

void demo_generic_Stack()

{

// an int value type argument...

gStack<int>^ is = gcnew gStack<int>( 10 );

for ( int ix = 0; ix < 10; ix++ )

is->push( ix*2 );

int elem_cnt = is->size();

for ( int ix = 0; ix < elem_cnt; ++ix )

Console::WriteLine( "({0}) {1}", ix+1, is->pop());

// a String^ reference type argument...

gStack<String^> ^ss = gcnew gStack<String^>( 10 );

ss->push( "Pooh" ); ss->push( "Piglet" );

ss->push( "Rabbit" ); ss->push( "Eeyore" );

elem_cnt = ss->size();

for ( int ix = 0; ix < elem_cnt; ++ix )

Console::WriteLine( "({0}) {1}", ix+1, ss->pop());

}

参数化类型对象的实际处理,例如 is 和 ss,与非参数化类型对象的处理完全一样。参数化类型的一个好处是单一的源定义能潜在地产生出无数种型实例。该例子中,generic 和 template 堆栈类在相同的参数化类源代码之外都支持字符串和整型类。在所有已知类型的应用程序中使用它们时没有真正的约束。正如你将会在后续专栏中看到的那样,并不是所有参数化类型都这样。

generic 和 template 定义以及种型实例在这里几乎是等价的,尽管并不是所有的参数化类型都这样。或者说在支持两种机制的 C++/CLI 中益处不多。当我在后续专栏中详细讨论两种机制时,你会看到其它一些差异。正是存在这些差异,在我遇到它们时,将它突出出来,而不是反复说其共性,似乎是整合其全貌的更好方法。

类型参数列表

每种类型参数都是以 class 或 typename 关键字开始的。这些关键字并包含任何平台意义——例如,class 并不是暗示要是一个本地类型,typename 也不是 意味着就是公共语言基础结构(CLI)类型。它们都表示紧跟着的名字是一个参数化类型的占位符,该占位符将会被用户指定的类型参数所取代。

之所以用中两个关键字是有历史原因的。在最初的模板规范中,Stroustrup 重用了现有的 class 关键字来指定一个类型参数而不是引入可能破坏已有程序的新关键字。直到 ISO-C++ 标准,class 关键字是声明类型参数的唯一方法。


重用现有的关键字似乎总是容易产生混淆。使用 class 来表示类型参数(parameter)是不是比内建类型和指针类型更能限制可用类型参数(arguments)成为 class 类型呢?不是,那么在这种情况下使用 class 就不会使人误解吗?肯定会的。所以,有些人觉得不引入新的关键字会导致不必要的混乱。但是那不是引入 typename 关键字的原因。

事实上,将 typename 引入 C++ 的真正的原因是为了支持模板定义的解析。这是个比较深入的话题,我在此只做一点简要介绍。详细描述请参考 Stroustrup 的 《Design and Evolution of C++》(Addison-Wesley, 1994)。

在某些情况下,要编译器来区分类型声明和表达式是不可能的。如果编译器遇到某个模板定义中的表达式Parm::name,并且 Parm 是一个表示 class 的模板类型参数,那么名称是该叫类型成员还是 Parm 的数据成员呢?

template <class Parm, class U>

Parm minus( Parm* array, U value )

{

Parm::name * p; // Is this a pointer declaration or

// a multiplication expression?

// By default treated as expression.

}

默认情况下,这个表示方法被认为是一个乘法表达式:运算符 Parm::name 乘以 p。关键字 typename 的引入使程序员能重写这种默认的解释。例如, 为了声明 Parm::name 类型的指针 p,可将模板函数重写如下:

template <class Parm, class U>

Parm minus( Parm* array, U value )

{

typename Parm::name * p; // ok: pointer declaration

}

既然这个关键字的存在已经是一种既定的事实,那么非要消除因重用 class 关键字而导致的混乱是很不明智的作法。公布的代码、书籍、文章、言论、论坛和出版物 都广泛使用它,因此不能对之视而不见。这就是为什么 C++ 对这两个关键字都支持的原因。

关键字 class 或 typename 的后面是一个标识符,它在 template 或 generic 定义充当占位符。在参数列表中的每一个标识符必须唯一。但是,在 交叉声明中这两个关键字和标识符是可以改变的:

template <class T>

public ref class tStack;

// ok: both the keyword and identifier can vary across

// declarations of the same type

template <typename elemType>

public ref class tStack {};

标识符的作用域用于持续类型声明的范围。在 tStack 的前向声明中,用分号结束,并且这个名字从没有被引用过。在实际定义中, 不论是在类定义中,还是在该类的每个以非内联(out-of-line)方式定义的成员函数中,这个标识符都是可见的。


类型实例化

template 或 generic 定义指明了当给定一个或多个实际类型集合时,如何构造单一的类或函数。实例化的时机是模板和泛型之间的一个主要区别之一。Template 的实例化是在编译时完成的;而 generic 的实例化是 CLR 在运行时完成的(在后面 专栏中,我会作更详细的介绍)。

template 定义做为一个自动产生特定类型实例的图解;编译器从字面上插入由用户提供的特定类型参数。而 generic 定义则更像是个蓝图;在运行时 构造特定类型实例,根据类型参数是引用还是值类型来修改常规语法。例如,用如下的代码,你可以从 template 和 generic 定义自动创建一个 int 类型的堆栈类对象和一个 String 类型的堆栈类对象: tStack<int> ^si;

tStack<String^> ^ss;

这个从模板定义中产生的类被称为模板实例化——在 ISO-C++ 标准中就是这样讨论的。在泛型的文字描述中,类的生成被称为构造——这 里又看到了模板与蓝图之间的不同之处。这里,我用“实例化”来描述这一过程。当 String 类型的堆栈类被实例化时,在 generic 或 template 定义中每每出现模板参数的地方都用 String 类型取代。该类型的正确性被验证。

实例化的名称是 Stack<int> 或 Stack<String^>。紧随名字后面的 <int> 或 <String^> 符号在ISO-C++中被称为模板参数。而在 generic 表述中 则称为类型参数,本文我将遵循这样的叫法。类型参数必须在用逗号分隔的列表中指定,并用尖括号括起来。实例化的名称必须要显式指定参数类型。与函数实例化类型参数不同,用于类实例化的类型参数决不能从所使用的类实例上下文来推断——其含义将在未来关于参数化函数和函数类型的专栏中讨论。

某个类的实例化可以在常规程序中任何使用非参数化类类型的地方使用,同样,某个实例化后的类对象的声明和使用与非参数化类完全相同。

最后,派生类和基类——绑定到独立类型参数的两个 generic(或 template)类型实例之间是没有特别关系的。认识这一点很重要。比如说,你不能在没有显式编程操作的情况下,初始化或将一个赋值给另一个。即便对整型实例化对象的非公有成员具有存取许可,你也不能进行堆栈 String 实例化操作。


上一页  1 2 
  • 上一篇教程: C++/CLI基本数据类型探索
  • 下一篇教程: VC编写简单的序列号(SN)填写器
  •  

    关于本站 | 广告联系 | 版权声明 | 使用帮助

    Copyright © 2004-2008 www.egunao.com All rights reserved.