注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

樱之花

叶散的时候,你明白欢聚;花谢的时候,你明白青春.

 
 
 

日志

 
 
关于我

分类中“我的实验室”是我在日常工作中的一些知识总结,有些写的比较匆忙,可能大家在阅读时会产生困扰,后期有时间我会重新整理编辑,谢谢大家的到访,您们的支持是我前进的动力!

网易考拉推荐

【引用】C++函数模板详解(转)  

2011-12-10 16:44:40|  分类: C++学习之路 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

http://blog.chinaunix.net/u1/45018/showart_353795.html

经常有碰到函数模块的应用,很多书上也只是略有小讲一下,今天又狂碰到函数模块,无奈特地找来C++编程经典<<C++ Primer>>翻阅一遍,终于有所全面了解.....

C++函数模块基础知识:

.  问题:

强类型语言要求我们为所有希望比较的类型都实现一个实例

int min( int a, int b ) {

return a < b ? a : b;

}

double min( double a, double b ) {

return a < b ? a : b;

}

有一种方法可替代为每个min()实例都显式定义一个函数的方法这种方法很有吸引力但是也很危险.那就是用预处理器的宏扩展设施例如  : #define min(a,b) ((a) < (b) ? (a) : (b))

在复杂调用的情况下,它的行为是不可预期的,这是因为它的两个参数值都被计算两次. 一次是在a b 的测试中另一次是在宏的返回值被计算期间.

#include <iostream>

#define min(a,b) ((a) < (b) ? (a) : (b))

const int size = 10;

int ia[size];

int main() {

int elem_cnt = 0;

int *p = &ia[0];

// 计数数组元素的个数

while ( min(p++,&ia[size]) != &ia[size] )

++elem_cnt;

cout << "elem_cnt : " << elem_cnt

<< "\texpecting: " << size << endl;

return 0;

}       

执行该程序的结果是下面不正确的计算结果:  elem_cnt : 5 expecting: 10

min()的宏扩展在这种情况下会失败因为应用在指针实参p 上的后置递增操作随每次扩展而被应用了两次

 

 

. 解决办法:

函数模板提供了一种机制通过它我们可以保留函数定义和函数调用的语义在一个程序位置上封装了一段代码确保在函数调用之前实参只被计算一次.

函数模板提供一个种用来自动生成各种类型函数实例的算法程序员对于函数接口参数和返回类型中的全部或者部分类型进行参数化(parameterize)而函数体保持不变.

下面是min()函数模板定义

template <class Type>

Type min( Type a, Type b ) {

return a < b ? a : b;

}

 

 

.  具体操作

关键字template 总是放在模板的定义与声明的最前面关键字后面是用逗号分隔的模板参数表template parameter list它用尖括号<> 一个小于号和一个大于号括起来该列表是模板参数表不能为空模板参数可以是一个模板类型参数template typeparameter它代表了一种类型也可以是一个模板非类型参数template nontype parameter它代表了一个常量表达式模板类型参数由关键字class typename 后加一个标识符构成在函数的模板参数表中这两个关键字的意义相同

 

模板非类型参数由一个普通的参数声明构成模板非类型参数表示该参数名代表了一个潜在的值而该值代表了模板定义中的一个常量例如size 是一个模板非类型参数它代表arr 指向的数组的长度

template <class Type, int size>

Type min( Type (&arr) [size] );

当函数模板min()被实例化时size 的值会被一个编译时刻已知的常量值代替。函数定义或声明跟在模板参数表后除了模板参数是类型指示符或常量值外函数模板的定义看起来与非模板函数的定义相同

template <class Type, int size>

Type min( const Type (&r_array)[size] )

{

/* 找到数组中元素最小值的参数化函数 */

Type min_val = r_array[0];

for ( int i = 1; i < size; ++i )

if ( r_array[i] < min_val )

min_val = r_array[i];

return min_val;

}

在程序的运行过程中Type 会被各种内置类型和用户定义的类型所代替而size 会被各种常量值所取代这些常量值是由实际使用的min()决定的记住一个函数的两种用法是调用它和取它的地址

当一个名字被声明为模板参数之后它就可以被使用了一直到模板声明或定义结束为止模板类型参数被用作一个类型指示符可以出现在模板定义的余下部分它的使用方式与内置或用户定义的类型完全一样比如用来声明变量和强制类型转换模扳非类型参数被用作一个常量值可以出现在模板定义的余下部分它可以用在要求常量的地方或许是在数组声明中指定数组的大小或作为枚举常量的初始值

 

 

 

 

四.几点注意

  如果在全局域中声明了与模板参数同名的对象函数或类型则该全局名将被隐藏在下面的例子中tmp 的类型不是double 是模板参数Type

typedef double Type;

template <class Type>

Type min( Type a, Type b )

{

// tmp 类型为模板参数 Type

// 不是全局 typedef

Type tmp = a < b ? a : b;

return tmp;

}

 

  在函数模板定义中声明的对象或类型不能与模板参数同名

template <class Type>

Type min( Type a, Type b )

{

// 错误: 重新声明模板参数 Type

typedef double Type;

Type tmp = a < b ? a : b;

return tmp;

}

 

  模板类型参数名可以被用来指定函数模板的返回位

// ok: T1 表示 min() 的返回类型

// T2 T3 表示参数类型

template <class T1, class T2, class T3>

T1 min( T2, T3 );

 

  模板参数名在同一模板参数表中只能被使用一次,但是模板参数名可以在多个函数模板声明或定义之间被重复使用

// 错误: 模板参数名 Type 的非法重复使用

template <class Type, class Type>

Type min( Type, Type );

 

// ok: 名字 Type 在不同模板之间重复使用

template <class Type>

Type min( Type, Type );

template <class Type>

Type max( Type, Type );

 

  如果一个函数模板有一个以上的模板类型参数则每个模板类型参数前面都必须有关键字class typename

// ok: 关键字 typename class 可以混用

template <typename T, class U>

T minus( T*, U );

// 错误: 必须是 <typename T, class U> <typename T, typename U>

template <typename T, U>

T sum( T*, U );

 

为了分析模板定义编译器必须能够区分出是类型以及不是类型的表达式对于编译器来说它并不总是能够区分出模板定义中的哪些表达式是类型例如如果编译器在模板定义中遇到表达式Parm::name Parm 这个模板类型参数代表了一个类那么name 引用的是Parm 的一个类型成员吗.

template <class Parm, class U>

Parm minus( Parm* array, U value )

{

Parm::name * p; // 这是一个指针声明还是乘法乘法

}

编译器不知道name 是否为一个类型因为它只有在模板被实例化之后才能找到Parm 表示的类的定义为了让编译器能够分析模板定义用户必须指示编译器哪些表达式是类型表达式告诉编译器一个表达式是类型表达式的机制是在表达式前加上关键字typename 例如如果我们想让函数模板minus()的表达式Parm::name 是个类型名因而使整个表达式是一个指针声明我们应如下修改

template <class Parm, class U>

Parm minus( Parm* array, U value )

{

typename Parm::name * p; // ok: 指针声明

}

关键字typename 也可以被用在模板参数表中以指示一个模板参数是一个类型

 

如同非模板函数一样函数模板也可以被声明为inline extern 应该把指示符放在模板参数表后面而不是在关键字template 前面

// ok: 关键字跟在模板参数表之后

template <typename Type>

inline

Type min( Type, Type );

 

 

******************************************************

 

http://hi.baidu.com/%C3%CE%C0%EF%CF%E3%B0%CD%C0%AD/blog/item/b7ad970ecebe5be636d12282.html

 

C++提供的函数模板可以定义一个对任何类型变量进行操作的函数,从而大大增强了函数设计的通用性。使用函数模板的方法是先说明函数模板,然后实例化成相应的模板函数进行调用执行。

函数模板的一般说明形式如下:

template < 模板形参表>

<返回值类型> <函数名>(模板函数形参表)

{

      //函数定义体

}

其中,<模板形参表>可以包含基本数据类型,也可以包含类类型。类型形参需要加前缀class。如果类型形参多于一个,则每个类型形参都要使用class。<模板函数形参表>中的参数必须是惟一的,而且<函数定义体>中至少出现一次。

函数模板定义不是一个实实在在的函数,编译系统不为其产生任何执行代码。该定义只是对函数的描述,表示它每次能单独处理在类型形式参数表中说明的数据类型。

例1:编写一个对具有n个元素的数组a[ ]求最小值的程序,要求将求最小值的函数设计成函数模板。

#include <iostream.h>

template <class T>

T min(T a[],int n)

{

int i;

T minv=a[0];

for(i=1;i<n;i++)

     if(minv>a[i])

       minv=a[i];

return minv;

}

void main()

{ int a[]={1,3,0,2,7,6,4,5,2};

   double b[]={1.2,-3.4,6.8,9,8};

   cout<<”a数组的最小值为:”

          <<min(a,9)<< endl;

   cout<<”b数组的最小值为:”

          <<min(b,4)<<endl;  

}

类模板与函数模板类似,它可以为各种不同的数据类型定义一种模板,在引用时使用不同的数据类型实例化该类模板,从而形成一个类的集合。

       类模板实际上是函数模板的推广。可以用相同的类模板来组建任何类型的对象集合。在传统C++中,可能有一个浮点数类或者一个整数类,如果使用类模板,可以定义一个对两者都适用的类number。

类模板说明

              类模板说明的一般形式是:

template  <类型形参表>

class  <类名>

{     //类说明体  

};

template  <类型形参表>

<返回类型> <类名> <类型名表>::<成员函数1>(形参表)

{     //成员函数定义体  }

template  <类型形参表>

<返回类型> <类名> <类型名表>::<成员函数2>(形参表)

{     //成员函数定义体  }

12.3.2   使用类模板

              与函数模板一样,类模板不能直接使用,必须先实例化为相应的模板类,定义该模板类的对象后才能使用。

              建立类模板后,可用下列方式创建类模板的实例:

              <类名> <类型实参表> <对象表>;

              其中,<类型实参表>应与该类模板中的<类型形参表>匹配。<类型实参表>是模板类(template class),<对象>是定义该模板类的一个对象。

              使用类模板可以说明和定义任何类型的类。这种类被称为参数化的类。如果说类是对象的推广,那么类模板可以说是类的推广。

              注意:类模板与模板类的区别 。

一个简单的类模板程序(求两个任意类型数的和)。

template<class T>

class A

{

public:

A();

A(T _a,T _b);

T sum();

private:

T a;

T b;

};

template <class T>

A<T>::A()

{

a=0;b=0;

}

template<class T>

A<T>::A(T _a,T _b)

{

a=_a;b=_b;

}

template<class T>

T A<T>::sum()

{

return (a+b);

}

void main()

{

A<int> ai(3,4);

A<double> ad(3.1,4.0);

cout<<ai.sum()<<" "<<ad.sum()<<endl;

}

程序已经在vc6.0下调试通过,以上代码在一个文件中,别把类的声明放在一个.h中把实现放在.cpp中,类模板不支持分离只能是类的声明和定义放在同一个文件中。

  评论这张
 
阅读(421)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017