函数模板和函数模板类模板
1、函数重载
首先它发
肯定是为了解决问题的,什么问题呢?
看下面代码
Myadd(int a,int b)
{
return a+b;
}
MYadd(double a,double b)
{
return a+b;
}
这样不同的数据类型,函数体内进行的操作确是相同的,会增加我们的工作量和维护成本,这时候,c++就发明了函数模板,具体语法如下:
template<class T1,class T2>//可以多写几个未知类型T看需要,注意:后面不能加;号,而且此声明只能用于下一个函数
//另外一种写法也可:template <typename T,typename T2>;
Myadd(T a,T b)
{
return a+b;
}
Myadd(int a,int b)
{
return a+b;
}
//当模板函数和普通函数发生重载时,编译器会优先调用普通函数
int main(void)
{
int a=2,b=3;
Myadd(a,b);//例如这里就是调用普通函数
double c=5;
Myadd (a,c);//这里也是调用普通函数,因为普通函数可以进行强制类型转化
//也可以强制指定调用模板函数
Myadd<>(a,c)//在函数参数前加<>,就可以强制调用模板函数了
//当然如果函数模板可以非常合适的匹配到输入参数则会优先执行模板函数
return 0;
}
//当然还有一些模板函数和普通函数重载的执行选择规则,但是一般我们不会产生很多的模板函数和普通函数重名在具体项目中
这是函数的重载
函数模板
仔细看上面的函数重载,我们可以发现,我们只是多了函数重载这个语法,当我们我们的函数只是输入参数不一样的时候,我们还是要重写这个函数,做一些重复的工作。
这个时候函数模板就可以来减少我们的工作量了,具体代码如下
#include <iostream>
using namespace std;
template <class T>//这里是模板函数的声明,只是针对下一行的函数
//T是定义的未知数据类型,具体语法就是 template <class T1,class T2>
//或者 template<typename T1,typename T2>
int MyAdd(T a,T b)
{
return a+b;
}
int main()
{
int a=1,b=2;
cout <<MyAdd(a,b)<<endl;
return 0;
}
利用函数模板的语法我们可以就一些只是输入值不一样的函数,只写一个函数模板来实现。
函数模板的具体工作原理是怎么样的呢?
编译器会对函数模板进行二次编译,第一次是不知道输入形参数据类型,对函数模板的这个模型来进行编译。
当具体函数在使用的时候,编译器会自己推到输入参数类型,根据具体的类型再由函数模板这个模型生产一个具体实例函数编译执行。
类模板和模板类
这里这个名词可能会有点绕,我们来理解一下,类模板强调的是模板,
template
class Person{};这里就是类模板,他就是一个模具,,模板类强调的是一个类,具体的类,,不过是用模具实例化处出来的
Person p1;这里就是模板类,一个具体的实例化的类
类模板的语法比函数模板复杂一些,不过我们把思路理清楚也不用当心。
- 第一点普通的类模板在定义的时候也是和函数模板一样的
template <class T>
class Person
{
public:
T m_age;
}
这就是类模板的具体声明了,和函数模板类似
那么区别在哪里呢,我们在函数模板的使用的时候是不用指定输入参数类型的,编译器会自己进行输入参数推导。
那么类模板具体实例化对象应该怎么办呢?
#include <iostream>
using namespace std;
template <class T>
class Person
{
public:
void Print_age(T age)//这里是类模板里面使用函数模板
{
this->m_age=age;
cout<<this->m_age<<endl;
}
public:
T m_age;
};
int main()
{
Person<int>p1;//类模板实例化对象语法
p1.m_age=10;
cout<<p1.m_age<<endl;
int a=10;
p1.Print_age(a);
return 0;
}
Personp1;//类模板实例化对象语法
我们还有什么问题呢?这个模板类中使用了一个函数模板,那么我们怎么实现类模板里面书写函数模板呢?
毕竟类模板里面书写函数模板是比较常见的(类的具体数据类型,那么类的成员函数的类型大概率也不能确定)
上面的代码书写了一个在类内书写函数模板的,下面的代码来书写一下类外函数模板的书写
#include <iostream>
using namespace std;
template <class T>
class Person
{
public:
void Print_age(T age);//这里是类模板里面使用函数模板
public:
T m_age;
};
template <class T>
void Person<T>::Print_age (T age)//具体语法是在类模板的函数类的实例化之前也要加一个 //template <class T>,再就是说明函数命名空间的时候加Person<T>::
//表示其属于类模板Person<T>,当然当类不是类模板而是一个普通类时就应 //该是 void Person::Print_age(T age)
{
this->m_age=age;
cout<<this->m_age<<endl;
}
int main()
{
Person<int>p1;
p1.m_age=10;
cout<<p1.m_age<<endl;
int a=10;
p1.Print_age(a);
return 0;
}
这里的类模板及其实例化,以及类模板里面的函数模板的二次编译很复杂,所以我们为了避免出错,
一般建议把类的声明和实现写在同一个文件,模板不适合分离定义和实现。
记住这一条我们就可以少犯很多错误,因为这个涉及模板的二次编译,一次是模板定义时,一个是模板被用来具体化对象/使用时,如果使用时编译器找不到定义的文件就麻烦了,因为编译器他时先把每个文件独立编译为汇编文件,再把工程里面的不同文件链接在一起。