在C++里,不考虑具体数据类型的编程模式叫做泛型编程,泛型也是一种数据类型,只不过它是一种用来代替所有类型的“通用类型”。
泛型编程通过函数模板和类模板来实现泛型编程。
9.1 函数模板
 9.1.1 函数模板的基本使用
 当我们想写个Swap()交换函数时,通常这样写:
imagepng
但是这个函数仅仅只能支持int类型,如果我们想实现交换double,float,string等等时,就还需要从新去构造Swap()重载函数,这样不但重复劳动,容易出错,而且还带来很大的维护和调试工作量。更糟的是,还会增加可执行文件的大小。
我们可以通过函数模板解决这个问题:
imagepng
Swap 泛型写法中的 T 不是一个具体的数据类型,而是泛指任意的数据类型。
函数模板其实是一个具有相同行为的函数家族
函数模板的语法规则如下
template   关键字用于声明开始进行泛型编程
 typename  关键字用于声明泛指类型
 imagepng
函数模板的应用
自动类型推导调用
 具体类型显示调用
 imagepng
9.1.2 深入理解函数模板
 1、为什么函数模板能够执行不同的类型参数?
其实编译器对函数模板进行了两次编译
第一次编译时,首先去检查函数模板本身有没有语法错误
第二次编译时,会去找调用函数模板的代码,然后通过代码的真正参数,来生成真正的函数。
所以函数模板,其实只是一个模具,当我们调用它时,编译器就会给我们生成真正的函数.
2、如何验证呢?
编译程序生成.o文件,然后通过nm命令查看符号表
g++ -c 函数模板.cpp nm 函数模板.o
imagepng
从符号表中可以非常直观的看到生成了两个不同的符号。
需要注意的是:函数模板是不允许隐式类型转换的,调用时类型必须严格匹配
也就说如下代码是非法的:
int a;
 float b;
 Swap(a, b);
 3、函数模板的特点
数模板还可以定义任意多个不同的类型参数,但是对于多参数函数模板:
编译器是无法自动推导返回值类型的
 可以从左向右部分指定类型参数
 #include 
using namespace std;
template 
 T1 add(T2 a, T3 b)
 {
 T1 ret;
 ret = static_cast(a + b);
 return ret;
 }
 void main()
 {
 int c = 12;
 float d = 23.4;
 //cout << add(c, d) << endl; //error,无法自动推导函数返回值
 cout << add(c, d) << endl; //返回值在第一个类型参数中指定
 cout << add
 }
 4、函数模板的重载
函数模板跟普通函数一样,也可以被重载
C++编译器优先考虑普通函数
 如果函数模板可以产生一个更好的匹配,那么就选择函数模板
 也可以通过空模板实参列表<>限定编译器只匹配函数模板
 #include 
using namespace std;
template `
 void fun(T a)
 {
 cout << “void fun(T1 a)” << endl;
 }
template 
 void fun(T1 a, T2 b)
 {
 cout << “void fun(T1 a, T2 b)” << endl;
 }
//函数模板的重载
 void fun(int a, float b)
 {
 cout << “void fun(int a, float b)” << endl;
 }
void main()
 {
 int a = 0;
 float b = 0.0;
 fun(a);
 fun(a, b); //普通函数void fun(int a, float b)已经能完美匹配,于是调用普通函数
 fun(b, a); //这个调用,函数模板有更好的匹配,于是调用函数模板
 fun<>(a, b); //限定只使用函数模板
 }
 5、总结
函数模板是泛型编程在C++中的应用方式之一
 函数模板能够根据实参对参数类型进行推导
 函数模板支持显示的指定参数类型
 函数模板是C++中重要的代码复用方式
 函数模板通过具体类型产生不同的函数
 函数模板可以定义任意多个不同的类型参数
 函数模板中的返回值类型必须显示指定
 函数模板可以像普通函数一样重载
 在任何一个函数模板前都必须 使用template 
 9.2 类模板
 9.2.1 类模板的定义
 还记得我们上次实现的Array类吗?在Array类中我们只能操作int类型的数据,如果需要操作char,float类型的数据我们该如何处理呢?难道我们再重新实现一个类吗?当然不用,我们可以使用类模板来实现。
在实际工作中,有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同,我们可以使用类模板来实现。
template
class 类名{
// 类定义......
};
 imagepng
imagepng
这样我们就定义了一个简单的类模板,其中的T代表任意的类型,可以出现在类模板中的任意地方,与函数模板不同的是,使用类模板构造对象时必须显示的指定数据类型,编译器无法自动推导,例如test
编译器对类模板的处理方式和函数模板相同
编译器从类模板通过具体类型产生不同的类
 编译器在声明的地方对类模板代码本身进行编译
 编译器在使用的地方对参数替换后的代码进行编译
 由于类模板的编译机制不同 , 所以不能像普通类一样分开实现后在使用时只包含头文件,在工程实践上 , 一般会把类模板的定义直接放到头文件中!!
只有被调用的类模板成员函数才会被编译器生成可执行代码!!!
imagepng
9.2.2 类模板的特化
 上面的类模板好像已经实现了add加法运算.但是却不能支持指针类型。其实,类模板也可以像函数重载一样, 类模板通过特化的方式可以实现特殊情况.
类模板特化:
表示可以存在多个相同的类名,但是模板类型都不一致(和函数重载的参数类似)
特化类型有完全特化和部分特化两种类型
完全特化表示显示指定类型参数,模板声明只需写成template<>,并在类名右侧指定参数,比如:
#include 
 using namespace std;
template < typename T1,typename T2 > //声明的模板参数个数为2个
 class Operator //正常的类模板
 {
 public:
 Operator()
 {
 cout << “Operator” << endl;
 }
 void add(T1 a, T2 b)
 {
 cout< }
 };
template <> //不需要指定模板类型,因为是完全特化的类模板
 class Operator
 {
 public:
 Operator()
 {
 cout << “Operator
 }
 void add(int a, int b)
 {
 cout< }
 };
int main()
 {
 //匹配完全特化类模板:class Operator< int,int>
 Operator
//匹配正常的类模板
 Operator
return 0;
 
}
 部分特化表示通过特定规则约束类型参数,模板声明和类相似,并在类名右侧指定参数,比如:
#include 
 using namespace std;
template < typename T1,typename T2 > //声明的模板参数个数为2个
 class Operator //正常的类模板
 {
 public:
 void add(T1 a, T2 b)
 {
 cout< }
 };
template < typename T > //有指定模板类型以及指定参数,所以是部分特化的类模板
 class Operator< T* ,T*> //指定类型参数,必须为2个参数,和正常类模板参数个数一致
 {
 public:
   void add(T* a, T* b)
   {
 cout<<*a+*b<
 };
int main()
 {
 Operator
 Operator
 return 0;
 }
 编译时,会根据对象定义的类模板类型,首先去匹配完全特化,再来匹配部分特化,最后匹配正常的类模板
 #include 
using namespace std;
template < typename T1,typename T2 >
 class Operator //正常的类模板
 {
 public:
 void add(T1 a, T2 b)
 {
 cout<<“add(T1 a, T2 b)”<
 };
template < typename T >
 class Operator
 {
 public:
 void add(T a, T b)
 {
 cout<<“add(T a, T b)”<
 };
template < typename T1,typename T2 >
 class Operator
 {
 public:
 void add(T1* a, T2* b)
 {
 cout<<“add(T1* a, T2* b)”<
 };
template < >
 class Operator
 {
 public:
 void add(void* a, void* b)
 {
 cout<<“add(void* a, void* b)”<
 };
int main()
 {
 int *p1 = new int(1);
 float *p2 = new float(1.25);
   Operator  Op1;        //匹配正常的类模板:class Operator  Op1.add(1,1.5);Operator  Op2;          //匹配部分特化的类模板:class OperatorOp2.add(1,4);Operator  Op3;      //匹配部分特化的类模板:class Operator  Op3.add(p1,p2);Operator  Op4;      //匹配完全特化的类模板:class OperatorOp4.add(NULL,NULL);  delete p1;delete p2;return 0;
        
}
 9.2.3 继承中类模板的使用
 9.2.3.1 父类是一般类,子类是模板类
class A {
 public:
 A(int temp = 0) {
 this->temp = temp;
 }
 ~A(){}
 private:
 int temp;
 };
template 
 class B :public A{
 public:
 B(T t = 0) :A(666) {
 this->t = t;
 }
 ~B(){}
 private:
 T t;
 };
 9.2.3.2 子类是一般类,父类是模板类
 template 
 class A {
 public:
 A(T t = 0) {
 this->t = t;
 }
 ~A(){}
 private:
 T t;
 };
class B:public A {
 public:
 //也可以不显示指定,直接A(666)
 B(int temp = 0):A(666) {
 this->temp = temp;
 }
 ~B() {}
 private:
 int temp;
 };
9.2.3.3 子类和父类都是模板类
 #include 
 using namespace std;
//1、类模板继承类模板
 template 
 class A
 {
 T1 x;
 T2 y;
 };
template 
 class B : public A
 {
 T1 x1;
 T2 y2;
 };
template 
 class C : public B
 {
 T x3;
 };
//2、类模板继承模板类
 template 
 class D : public A
 {
 T x4;
 };
 //3、类模板继承普通类
 class E
 {
 int x4;
 };
 template 
 class F : public E
 {
 T X5;
 };
                    上一篇:R语言生物群落数据统计分析