visual studio 2005把泛型编程的类型参数模型引入了微软.net框架组件。c++/cli支持两种类型参数机制--通用语言运行时(clr)泛型和c++模板。本文将介绍两者之间的一些区别--特别是参数列表和类型约束模型之间的区别。
参数列表又回来了
参数列表与函数的信号(signature)类似:它标明了参数的数量和每个参数的类型,并把给每个参数关联一个唯一的标识符,这样在模板定义的内部,每个参数就可以被唯一地引用。
参数在模板或泛型的定义中起占位符(placeholder)的作用。用户通过提供绑定到参数的实际值来建立对象实例。参数化类型的实例化并非简单的文本替代(宏扩展机制就是使用文本替代的)。相反地,它把实际的用户值绑定到定义中的相关的形式参数上。
在泛型中,每个参数都表现为object类型或衍生自object的类型。在本文后面你可以看到,这约束了你可能执行的操作类型或通过类型参数声明的对象。你可以通过提供更加明确的约束来调整这些约束关系。这些明确的约束引用那些衍生出实际类型参数的基类或接口集合。
模板除了支持类型参数之外,还支持表达式和模板参数。此外,模板还支持默认的参数值。这些都是按照位置而不是名称来分解的。在两种机制之下,类型参数都是与类或类型名称关键字一起引入的。
参数列表的额外的模板功能
模板作为类型参数的补充,允许两种类型的参数:非类型(non-type)参数和模板参数。我们将分别简短地介绍一下。
非类型参数受常数表达式的约束。我们应该立即想到它是数值型或字符串常量。例如,如果选择提供固定大小的堆栈,你就可能同时指定一个非类型的大小参数和元素类型参数,这样就可以同时按照元素类别和大小来划分堆栈实例的类别。例如,你可以在代码1中看到带有非类型参数的固定大小的堆栈。
代码1:带有非类型固定大小的堆栈template <class elemtype, int size>
public ref class tstack
{
array<elemtype> ^m_stack;
int top;
public:
tstack() : top( 0 )
{ m_stack = gcnew array<elemtype>( size ); }
};
此外,如果模板类设计者可以为每个参数指定默认值,使用起来就可能方便多了。例如,把缓冲区的默认大小设置为1kb就是很好的。在模板机制下,可以给参数提供默认值,如下所示:// 带有默认值的模板声明
template <class elemtype, int size = 1024>
public ref class fixedsizestack {};
用户可以通过提供明确的第二个值来重载默认大小值:// 最多128个字符串实例的堆栈
fixedsizestate<string^, 128> ^tbs = gcnew fixedsizestack<string^, 128>;
否则,由于没有提供第二个参数,它使用了相关的默认值,如下所示:// 最多1024个字符串实例的堆栈
fixedsizestack<string^> ^tbs = gcnew fixedsizestack<string^>;
使用默认的参数值是标准模板库(stl)的一个基本的设计特征。例如,下面的声明就来自iso-c++标准:// iso-c++名字空间std中的默认类型参数值示例
{
template <class t, class container = deque<t> >
class queue;
template <class t, class allocator = allocator<t> >
class vector;
// ...
}
你可以提供默认的元素类型,如下所示:// 带有默认的元素类型的模板声明
template <class elemtype=string^, int size=1024>
public ref class tstack {};
从设计的角度来说很难证明它的正确性,因为一般来说容器不会集中在在单个默认类型上。
指针也可以作为非类型参数,因为对象或函数的地址在编译时就已知了,因此是一个常量表达式。例如,你可能希望为堆栈类提供第三个参数,这个参数指明遇到特定条件的时候使用的回调处理程序。明智地使用typedef可以大幅度简化那些表面上看起来很复杂的声明,如下所示:typedef void (*handler)( ... array<object^>^ );
template <class elemtype, int size, handler cback >
public ref class tstack {};
当然,你可以为处理程序提供默认值--在这个例子中,是一个已有的方法的地址。例如,下面的缓冲区声明就提供了大小和处理程序:void defaulthandler( ... array<object^>^ ){ ... }
template < class elemtype,
int size = 1024,
handler cback = &defaulthandler >
public ref class tstack {};
由于默认值的位置次序优先于命名次序,因此如果不提供明确的大小值(即使这个大小与默认值是重复的),就无法提供重载的处理程序的。下面就是可能用到的修改堆栈的方法:void demonstration()
{
// 默认的大小和处理程序
tstack<string^> ^ts1 = nullptr;
// 默认的处理程序
tstack<string^, 128> ^ts2 = gcnew tstack<string^, 128>;
// 重载所有的三个参数
tstack<string^, 512, &yourhandler> ^ts3;
}
模板支持的第二种额外的参数就是template模板参数--也就是这个模板参数本身表现为一个模板。例如:// template模板参数
template <template <class t> class arena, class arenatype>
class editor {
arena<arenatype> m_arena;
// ...
};
editor模板类列出了两个模板参数arena和arenatype。arenatype是一个模板类型参数;你可以传递整型、字符串型、自定义类型等等。arena是一个template模板参数。带有单个模板类型参数的任何模板类都可以绑定到arena。m_arena是一个绑定到arenatype模板类型参数的模板类实例。例如:// 模板缓冲区类
template <class elemtype>
public ref class tbuffer {};
void f()
{
editor<tbuffer,string^> ^texteditor;
editor<tbuffer,char> ^bliteditor;
// ...
}
Java Asp PHP .Net XML C/C++ CGI VB Jsp J2ee J2se J2me EJB Servlet Tomcat Resin Struts Weblogic Eclipse ANT GUI JMS Web servise IDEA Webphere Hibernate Spring Jboss Applet Swing Socket Javamail Perl Ajax P2P 安全 模式 框架 测试 开源 游戏
Windows XP Windows 2000 Windows 2003 Windows Me Windows 9.x Linux UNIX 注册表 操作系统 服务器 应用服务器