C++之函数模板

本文详细介绍了C++中的函数模板,包括其概念、语法、实例化过程、使用注意事项及函数模板重载。通过示例代码展示了如何利用函数模板实现代码复用,提高效率,并讨论了模板特化。此外,还探讨了类模板及其与模板类的区别,以及派生类与类模板的组合使用。最后分析了模板的优缺点,如代码复用和灵活性增强,但同时也可能导致代码复杂性和编译错误排查的困难。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.什么是函数模板

     在介绍函数模板前先看段代码:


 

对,就是函数重载,我们知道函数重载基于不同数据类型实现类似操作,那么如果数据类型比较多的时候怎么办呢?只要有新类型出现,就要重新添加对应函数,而且代码函数体都相同,但代码复用率却不高。那么可不可以使用预处理指令,可以,但是安全性却不高。所以我们引入函数模板。那什么是函数模板?

函数模板:

代表了一个函数家族,该函数与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。

 

 

.函数模板的语法:

函数模板的定义格式:

template<typename Param1, typename Param2,...,class Paramn>

返回值类型 函数名(参数列表)

{

...

}

其中template是声明函数模板的关键字。<typename Param1, typename Param2,...,class Paramn>为模板参数表 typename定义模板参数的关键字当然也可以是class(但不可以使用struct,

模板函数也可定义成内联函数:

template<typename T>

inline T Add(const T _left, const T _right)

{

return (_left + _right);

}

注意:inline关键字必须放在模板形参表之后,返回值之前,不能放在template之前。

 

**模板是一个蓝图,它本身不是类或者函数,编译器用模板产生指定的类或者函数的特定类型版本,产

生模板特定类型的过程称为函数模板实例化。

函数模板实例化:

template <typename T>

void swap(T &x,T &y)

{

  T temp;

  temp = x;

  x = y;

  y = temp;

}

void main()

{

  int x = 8;

  int y = 6;

  cout <<"交换位置前x,y的值x =" << x <<"  " << "y = "<< y << endl;

  swap(x,y);

  cout <<"交换位置后x,y的值x =" << x <<"  " << "y = "<< y << endl;

  float a = 2.6f;

  float b = 5.8f;

  cout <<"交换位置前a,b的值a =" << a <<"  " << "b = "<< b << endl;

  swap(a,b);

  cout <<"交换位置后a,b的值a =" << a <<"  " << "b = "<< b << endl;

  char ch1,ch2;

  ch1 = 'a';

  ch2 = 'b';

  cout <<"交换位置前ch1,ch2的值ch1 =" << ch1 <<"  " << "ch2 = "<< ch2 << endl;

  swap(ch1, ch2);

  cout <<"交换位置前ch1,ch2的值ch1 =" << ch1 <<"  " << "ch2 = "<< ch2 << endl;

  system("pause");

}

 

如这个程序当编译器遇到swap(x,y)时,会根据模板的函数声明,生成实例函数。这样可以使得一段程序用于处理多种不同类型的对象,从而实现真正的代码可重性。

 

 

 

三.使用函数模板需要注意的问题

   1.函数模板中的每一个类型参数在函数参数表中至少使用一次。如下声明即错误声明:template <class T1, class T2>

          Void func1(T1 paral)

          {

          }

   2.全局域中声明的与模板参数同名的对象、函数、类型,在函数模板中将被隐藏。如:

 int paral;//全局变量

 template <class T1>

 Void func2(T1 paral)

{

  //函数体,在函数体中访问的是T1类型中的paral而不是全局整型变量paral

}

  3.函数模板定义中声明的对象或类型不能与模板参数同名。如:

template <class T>

T max(T x,T y)

{

  typedef float T;    //定义的类型与模板参数同名

}

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

 5)模板参数名在同一模板参数表中只能使用一次,但可在多个函数模板声明或定义之间重复使用。如:

  template <class T,class T>  // 模板参数名重复使用

  T max (T x,T y)

  template <class T>

  T max(T x,T y)

  template <class T> //模板参数名T在不同模板之间重复使用

  T min(T x,T y)

 6.一个模板的定义和多处声明所使用的模板参数名无须相同。如:

  template <class T>   //模板的前向声明

  T max(T x, T y);

  template <class Type>  //定义和多处使用的模板参数名无须相同

  Type max (Type x,Type y)

  {

    //函数体

  }  

(7).函数模板如果有多个模板类型参数,则每个模板类型参数前面都必须用关键字classtypename 修饰,且可以混用。

(8)模板参数在函数参数表中出现的次数没有限制。

9)在模板形参内部不能指定缺省的模板实参

 

模板函数重载:

int Max(const int& left, const int & right)

{

return left>right? left:right;

}

template<typename T>

T Max(const T& left, const T& right)

{

return left>right? left:right;

}

template<typename T>

T Max(const T& a, const T& b, const T& c)

{

return Max(Max(a, b), c);

};

int main()

{

Max(10, 20, 30);

Max<>(10, 20);

Max(10, 20);

Max(10, 20.12);

Max<int>(10.0, 20.0);

Max(10.0, 20.0);

return 0;

}

模板函数特化:

在说模板函数特化的时候我们先来看看这个程序

template <typename T>

int compare(T t1,T t2)

{

  if (t1 < t2)

  return -1;

  if(t1>t2)

  return 1;

   return 0;

}

int main()

{

  char *pStr1 = "1234";

  char *pStr2 = "abcd";

  cout <<compare(pStr1,pStr2)<<endl;

  return 0;

  system("pause");

}

程序正常情况下应该返回-1,可是你可以试试运行一下这个程序

 

是不是和你想想中的不一样?那么到底是为什么呢?其实导致这个结果的原因是因为:函数在调用的时候将俩个指针变量的地址传递给模板函数,在比较时比较的是俩个地址,不是俩个值。怎么来解决这个问题呢?

 

 

    

这样就可以了。

模板函数特化形式如下:

1、关键字template后面接一对空的尖括号<>

2、再接模板名和一对尖括号,尖括号中指定这个特化定义的模板形参

3、函数形参表

4、函数体

template<>

返回值 函数名<Type>(参数列表)

{

// 函数体

}

**在模板特化版本的调用中,实参类型必须与特化版本函数的形参类型完全匹配,

如果不匹配,编译器将为实参模板定义中实例化一个实例。

 

 

四.类模板

     类模板主要表示像数组、链表、矩阵等这类数据结构或包含有通用得逻辑算法,类模板就变得特别有用。

     类模板的定义语法格式:

template <class T1,class T2,......>

Class 类名

{

  类成员声明

}

除此之外,类模板成员函数的定义在类模板以外,形式如下:

template <class T1class T2.....>

类型名 类名<T1,T2,....>::函数名(参数表)

其中template 是声明类模板的关键字。< , , ..>是模板参数表,模板参数分为模板类型参数和模板非类型参数

下面看个例子:

普通顺序链表的实现:typedef int DataType;

//typedef char DataType;

class SeqList

{

private :

DataType* _data ;

int _size ;

int _capacity ;

};

以类模板实现动态链表:template<typename T>

class SeqList

{

public :

SeqList();

~ SeqList();

private :

int _size ;

int _capacity ;

T* _data ;

};

template <typename T>

SeqList <T>:: SeqList()

: _size(0)

, _capacity(10)

, _data(new T[ _capacity])

{}

template <typename T>

SeqList <T>::~ SeqList()

{

delete [] _data ;

}

void test1 ()

{

SeqList<int > sl1;

SeqList<double > sl2;

}

看出来什么了吗?类模板的定义几乎和普通类的定义相同,只不过在类模板的定义中要加上模板定义的关键字和模板参数列表。

下面看个成员函数实例化的例子:

struct student

{

   int id;

   int score;

};

template <class T>

class buffer

{

private:

    T a;

    int empty;

public:

buffer(void);

T get(void);

void put(T x);

};

template <class T>

buffer <T>::buffer(void):empty(0){}  //成员函数初始化

template <class T>

T buffer <T>::get(void)

{

    if(empty == 0)

{

  cout <<"the buffer is empty!"<< endl;

  exit(1);

}

return a;

}

template <class T>

void buffer <T>::put(T x)

{

  empty++;

  a = x;

}

void main(void)

{

student s = {1022,78};

buffer <int>i1,i2;

buffer <student> stu1;

buffer <double> d;

i1.put(13);

i2.put(-101);

cout << i1.get() <<" " <<i2.get()<<endl;

stu1.put(s);

cout <<"the student's id is "<<stu1.get().id<<endl;

cout <<"the student's score is "<<stu1.get().score<<endl;

cout <<d.get() <<endl;

system("pause");

}

执行结果如下:


 

类模板实例化的特点:

1.只有当类模板实例真正使用时,编译器才会实例化类模板。

2.实例化类模板时,除了构造函数和析构函数外,不会自动实例化类模板的其他成员函数。

3.对于模板非类型参数,实例化时赋给的实参必须是编译时常量,否则会出错。

 

类模板与模板类:

1类模板是抽象带有相同功能的类,不能定义对象,因为这些类的成员函数返回值、成员函数形式参数以及数据成员的数据类型不同。而模板类是类模板的实例

2类模板是程序员写出来的,而模板类是编译器自动生成的。

3.类模板是模板的定义,不是一个实实在在的类,而模板类具有程序代码,占用内存空间,可以实际执行,模板类是实实在在的类。

下面看一个基类和派生类使用不同数据类型的简单例子:

template <typename T>

class A

{

public :

void f(T a)

{

 cout <<a <<endl;

}

};

template <typename x, typename y>

class B:public A<y>

{

public :

void g(x b)

{

  cout <<b <<endl;

}

};

void main()

{

 B <char, int >bb;

 bb.f(10);

 bb.g('A');

 B<int, int>bb1;

 bb1.f(10);

 bb1.g(25);

 system("pause");

 

}

执行结果:

 

 

 

五.派生类和类模板

派生类和类模板一般有三种组合

1.从类模板派生类模板:建立类模板的层次结构

2.从类模板派生非类模板:从类模板创建具体的类,而不是编译器自动创建不满足要求的类。

3.从非类模板派生出类模板:从现存类中创建类模板。


 

 

 

使用模板的优缺点分析:

优点:

模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生。

增强了代码的灵活性。

缺点:

模板让代码变得凌乱复杂,不易维护,编译代码时间变长。

出现模板编译错误时,错误信息非常凌乱,不易定位错误。   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值