C++之模板

最近因为工作原因,需要掌握C++中模板的用法,故分三部分作简单学习笔记如下:

1.函数模板

模板可以让我们生成通用参数类型的函数,这个通用的函数能够接受任意类型参数,同样可以返回任意类型的值,这样就避免了对所有类型的函数进行重载。
譬如说:我们可能在业务开发中会用到类似如下函数
 1 #include<iostream>
 2 using namespace std
 3 
 4 int Plus(int a,int b)
 5 {
 6     int Sum;
 7     Sum = a + b;
 8     return Sum;
 9 }
10 
11 double Plus(double a,double b)
12 {
13     double Sum;
14     Sum = a + b;
15     return Sum;
16 }
如上所示,当我们在计算两个数之和时,常常会遇到整型和浮点型两种不同的参数,这样,我们就不得不使用函数的重载来实现。如果假设,我们某一个接口函数有很多不同类型的入参,都有相同的实现,那么我们将会设计很多重载来实现,这样将会带来很多重复性的工作。而我们程序员就是要消灭这种重复性的工作。所以,C++中引入了函数模板的概念。
模板,是将一个事物的结构规律予以固定化,规律化的成果。既然在程序中,大家的实现都一致,只是入参和返参不同,那么我们就定义一个通用的入参和返参,接口内部同样一种实现。如此,就产生了函数模板。接下来,我们在代码中认识一下它。
1 template <class Name> Function
2 template <typename Name> Function
如上所示,这就是一个函数模板的定义,首先我们要有关键字template,后面紧跟一个尖括号,里面关键字可以使用class或typename,后面的Name是由我们自己定义的名称,最后面Function就是我们的函数实现了。就拿前文中提到的计算两个数之和的函数来举例,我们来看下如果使用函数模板是如何实现的:
 1 #include<iostream>
 2 using namespace std;
 3 template <class G_Type>
 4 G_Type Plus(G_Type a, G_Type b)
 5 {
 6     G_Type Sum;
 7     Sum = a + b;
 8     return Sum;
 9 }
10 int main()
11 {
12     int a = 10, b = 20, Sum1;
13     double c = 3.14, d = 6.28, Sum2;
14     Sum1 = Plus(10,20);
15     Sum2 = Plus(3.14,6.28);
16     cout << Sum1 << endl;
17         cout << Sum2 << endl;
18     return 019 }
运行后结果:
 
以上,我们使用了一个函数模板的方式省去了写多个重载函数的时间。
补充:
上述我们的模板只有一个通用类型参数即G_Type,所以,当我们定义好之后,只能使用同样的类型参数去调用模板函数,譬如都是整型,或都是浮点型,那如果说我们想用不同类型参数去调用这个函数呢?一起来看下面的例子
 1 Sum1 = Plus(10,20);                 //可实现
 2 Sum2 = Plus(3.14,6.28);           // 可实现
 3 Sum3 = Plus(3.14,10);             //不可实现,这种情况在上述模板就无法实现,为了应对这种多种类型的参数,我们需要对函数模板进行小的修改。
 4 template <class G_Type,class U_Type>
 5 G_Type Plus(G_Type a, U_Type b)
 6 {
 7     G_Type Sum;
 8     Sum = a + b;
 9     return Sum;
10 }
如上,我们新增一个通用类型名为U_Type,这样我们就可以将函数模板适用于多参数。
 

2.类模板

上述我们学习了函数模板,我们同样也可以实现类模板。类模板的实现,可以使类有通用类型的成员,不必在定义类的时候定义成员的类型,即类的实现不关注数据元素的具体类型,而只关注类所需要实现的功能。
首先,我们还是在程序中认识一下类模板:
//类模板实现
template<class Name>
class Student
{
public:
    Name m_Member;//成员函数定义
public:
    Name m_Add(Name a, Name b);//成员函数声明
};
如上所示,这就是一个类模板的定义,首先我们还是要有关键字template,后面紧跟一个尖括号,里面关键字可以使用class或typename,后面的Name是由我们自己定义的名称,下面就是我们的Student类的实现了。我们仍旧使用前文中提到的计算两个数之和的函数来举例,这一次我们来看下如果使用类模板又是如何实现的:
 1 #include <stdio.h>
 2 
 3 //类模板实现
 4 template<class Name>
 5 class Student
 6 {
 7 public:
 8     Name m_Member;//成员函数定义
 9 public:
10     Name m_Add(Name a, Name b);//成员函数声明
11 };
12 
13 //成员函数的外部实现
14 template <class Name>
15 Name Student<Name>::m_Add(Name a, Name b)
16 {
17     return a + b;
18 }
19 
20 //主函数
21 int main()
22 {
23     Student<int> stu;
24     printf("stu.m_Add(1,2) = %d \n",stu.m_Add(1,2));
25     return 0;
26 }
运行后结果:
 
 
以上,我们就认识了类模板,并且学会了简单的应用。
补充:
类模板和函数模板一样,同样可以定义多种类型不同的参数,下面我们还是用程序代码来说明
 1 //类模板实现
 2 template<class Name1,class Name2>
 3 class Student
 4 {
 5 public:
 6     Name1 m_Member1;
 7     Name2 m_Member2;//成员函数定义
 8 public:
 9     void m_Add(Name1 a, Name2 b);//成员函数声明
10 };
11 
12 //成员函数的外部实现
13 template <class Name1,class Name2>
14 void Student<Name1, Name2>::m_Add(Name1 a, Name2 b)
15 {
16     return a + b;
17 }

3.模板的特化

a.函数模板特化
函数模板在某种特定类型下的特定实现称为函数模板的特化。
 1 template <typename T>
 2 T add(T a, T b)
 3 {
 4     printf("T add(T a, T b)\n");
 5     return  a + b;
 6 }
 7 //上述函数不支持int*类型,所以要对传入指针类型参数进行特化。
 8 template <>
 9 int* add<int*>(int* pa, int* pb)
10 {
11     printf("T add(const int* pa, const int* pb)\n");
12     *pa += *pb;
13     return pa;
14 }
15 
16 int main(void)
17 {
18     int a = 8, b = 6;
19     add<>(&a, &b);    //<>,编译器会自动推导数据类型为int*
20 
21     return 0;
22 }
b.类模板的特化
类模板特化有部分特化与完全特化
首先定义一个类模板和类的成员外部实现
 1 template <typename T1, typename T2>
 2 class TestCls
 3 {
 4 public:
 5     void add(T1 a, T2 b);
 6 };
 7 
 8 template <typename T1, typename T2>
 9 void TestCls <T1, T2>::add(T1 a, T2 b)
10 {
11     printf("estCls <T1, T2>::add(T1 a, T2 b) = %d\n", a + b);
12 }
13 
14 //部分特化
15 template <typename T>
16 class TestCls <T, T>
17 {
18 public:
19     void add(T a, T b);
20 };
21 
22 template <typename T>
23 void  TestCls <T, T>::add(T a, T b)
24 {
25     printf("TestCls <T, T>::add(T a, T b) = %d\n", a + b);
26 }
27 
28 int main(void)
29 {
30     TestCls<int, int> t;    //编译器会选择特化后的类。
31                             //注意<>内不能为空,编译器对类模板不能进行类型推导。
32     t.add(5, 5);
33     return 0;
34 }
以上称为部分特化,我们原先的设定是T1、T2两种类型,二者既可以是同一种类型,也可以是不同一种,特化后T1、T2只能是相同的一种类型T。当我们定义的对象的T1、T2类型是一样的时候,编译器会选择特化后的类模板
 1 //完全特化,完全特化后"<>"内为空
 2 template <>
 3 class TestCls<int, char>        //直接指定了T1、T2的类型
 4 {
 5 public:
 6     void add(int a, char b)
 7     {
 8         printf("TestCls<int, char>::add(int a, char b)\n");
 9     }
10 };
11 
12 int main(void)
13 {
14     TestCls<int, char> t;
15     t.add(5, 5);
16     return 0;
17 }

 

以上,即本次学习模板后的学习笔记,后续深入学习再持续补充,上述内容如有不足之处,望各位读者批评指正。

 

 

leo zhai

Nov,23.2018

leonardozhai@outlook.com

 
模板特化部分参阅以下两篇文章:
c++的函数模板-作者:bright261
C++ 高级篇(一)—— 模板(Templates)-作者:zqixiao_09
 
posted @ 2018-11-23 09:26  不万能的阿司匹林  阅读(946)  评论(0编辑  收藏  举报