首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

6.7 软件工程:复用和泛型

编写和测试新代码总是很昂贵,所以复用是降低软件成本的关键,而模板是提供有效的复用代码的一种重要方式。与#define宏不同,模板是类型安全的并且可以进行适当地限定。

正如本章所指出,开发模板通常并不比开发特定的代码更昂贵。开发模板的典型方法是:程序员先为一个给定类型编写专用的代码,这个给定的类型应该包括可以对泛型代码实例化的各种不同类型的特性。这段专用的代码应该仔细地进行调试和测试直到程序员满意为止,然后程序员再使用模板格式重写这段特定的代码。

6.7.1 调试模板代码

使用普通代码时,会得到一些编译器发现的语法错误,以及程序员希望找到的运行时错误。使用模板代码时,语法错误通常有两类:一类仅仅是不考虑类型实例化的错误,另一类是因为特殊的实例化产生的错误。下面是在本章前面编写的一些代码;但是这里进行了一点修改,在一条语句中遗失了分号,因此产生了一个语法错误:

template <class T1, class T2>

bool coerce(T1& x, T2 y)

{

   if (sizeof(x) <= sizeof(y))

      return false                             // missing semicolon

   x = static_cast<T1>(y);

   return true;

}

这类错误很容易在编译时检测出来。现在考虑下面这段改正了分号错误的代码:

int main()

{

   int i = 6;

   struct s { int first, second; }x;

   coerce(i, x);

   cout << i;

}

这段代码再次出现语法错误,但是这个错误是类型实例化错误,代码中要将一个struct变量强制转换为一个int型变量。不同的编译器会使用一些很难理解的信息来标注这个错误。不管读者使用的是什么C++编译器,应该尝试一下该代码。

int main()

{

   int i = 6;

   double x = 5.5;

   coerce(i, x);

   cout << i;

}

这段代码能够正常工作,但是Borland编译器通常会指出这是一段不可达(unreachable)代码,原因是测试语句

if (sizeof(x) <= sizeof(y))

中的x和y分别被实例化为int类型和double类型,因此表达式总是为真。不用说,代码是泛型代码,但是它使用了强制类型转换,所以这段代码是高度不可信任的。作为软件工程的普遍规则,应该远离那些可能不可移植的代码。

6.7.2 特殊考虑

许多当前的C++模板实现区分了函数模板参数和类模板参数。函数模板只允许使用类参数,它们必须作为至少一个函数参数的类型描述出现在模板函数签名中。下列写法是正确的:

template <class TYPE>

void maxelement(TYPE a[], TYPE& max, int size);

template <class TYPE>

int find(TYPE* data);

下面这段代码以前是不合法的,但是根据ANSI标准,现在则是合法的:

template <class TYPE>

TYPE convert(int i) { TYPE temp(i); return temp; }

在ANSI标准中,函数可以像下面这样调用:

// Newly allowed explicit function instantiation

convert<double>(i + j);

因为该用法以前并不合法,所以一些系统可能不支持这种函数实例化。存在这些限制的原因是编译器必须根据函数调用时的参数才能推断出创建哪个函数。一个工作区可能是通过创建一个类生成的,这个类的唯一成员是一个参数化的静态函数,例如:

template <class TYPE>                             // other arguments possible

class convert_it {

public:

   static TYPE convert(int i)

            { TYPE temp(i); return temp; }

};

int main()

{

   convert_it<double> D;

   cout << D.convert(5) << endl;              // outputs a double

}

6.7.3 使用typename

可以在模板内部使用关键字typename代替关键字class来声明模板类型参数。当class不能让模板声明得到正确语法分析时,必须使用关键字typename。

template<class T1, class T2>

T1 foo(T1* w, T2 data)

{

   typename T1:: y * z;              // a pointer declaration

   ......

};

没有typename声明,编译器将不知道foo()中是声明了一个T1::y*类型的z还是将y乘以z。另外还有一些的微妙情况也需要这种形式。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论