作用:
减少程序中的冗余信息。如:多个函数或类的除了参数类型外,其余都完全相同时,可以使用模板来减少重复信息(参考函数重载时,输入参数数量也相同的情况)
1、函数模板
即建立一个通用函数,只不过该函数的返回类型和形参类型都不具体指定,其定义格式如下:
template <typename 类型名>
类型名或void 函数名(类型名 形参表)
{
函数体
}
或
template <class 类型名>
类型名或void 函数名(类型名 形参表)
{
函数体
}
注:
1、此处的类型名是由开发人员自己决定的。
2、一般不注重向后兼容且愿意输入较长的类型名时,选用typename。
代码示例:
#include <iostream>
#include <string>
using namespace std;
template <typename ty_na>
ty_na func_name(ty_na* parameters, int size = 0) {
ty_na first_num = parameters[size];
return first_num;
}
int main()
{
int array_int[] = {1,2,3,4,5};
int first_num = func_name(array_int, 0);
cout << first_num << endl;
return 0;
}
此外,由于函数模板也属于函数,因此也可以对其进行函数重载,重载时,参数和参数数量均可改变:
#include <iostream>
#include <string>
using namespace std;
template <class type>
type func_name2(type a, type b)
{
type c = a - b;
return c;
}
template <class type>
type func_name2(type x, type b, type k)
{
type y = k * x + b;
return y;
}
int main()
{
int get_c = func_name2(1, 2);
double get_y = func_name2(2.0, 3.2, 2.0);
cout << "get_c=" << get_c << endl;
cout << "get_y=" << get_y << endl;
}
2、类中函数模板
2.1先在头文件的对应类中声明模板函数
class cls1
{
public:
template<typename T>
void func(T &args);
}
2.2在对应cpp文件中定义模板函数
template<typename T>
void cls1::func(T &args)
{
函数体
}
3、类模板
3.1 基本概念
格式:
template <template 类型名>
class 类名
{
public:
类型名 成员数据
成员函数(函数类型名 参数名)
}
当类模板中的成员函数需要在类外定义时,格式为:
template <typename 类型名>
函数类型名 类名<类型名>::成员函数名(类型名 形参表) {...}
示例:
#include <iostream>
#include <string>
using namespace std;
template <typename num>
class compute
{
public:
num a, b;
compute(num x, num y) :a(x), b(y) {};
num add();
};
template <typename num>
num compute<num>::add(){
return a + b;
}
int main()
{
compute<int> result(2, 3);
cout << result.add() << endl;
}
此外,类模板可以对模板类型指定默认类型,如:
template <typename TYPE=int>
class CLS{...}
注意:必须使用显示类型指导调用类模板进行实例化,否则类模板无法识别类型。当类模板有类型默认参数时,可以不实例化对应类型。
template <class Name_T,class Age_T = int>
class Person {
public:
Person(Name_T name, Age_T age) {
this->m_Name = name;
this->m_Age = age;
}
void showPerson() {
cout << "name:" << this->m_Name << " ";
cout << "age:" << this->m_Age << endl;
}
private:
Name_T m_Name;
Age_T m_Age;
};
int main() {
//Person p1("马蒂", 22);
//使用显示类型指导调用类模板进行实例化,否则类模板无法识别类型
Person<string, int> p1("马蒂", 22);
p1.showPerson();
Person<string> p2("诺顿", 78);//当类模板有类型默认参数时,可以不实例化对应类型
p2.showPerson();
system("pause");
return 0;
}
3.2 类模板中成员函数的创建时机
一般类中的成员函数是一开始就创建,而类模板中的成员函数在调用时才会被创建。
如:下列代码可以生成成功,但在实际运行时必定会报错,这是因为类模板的成员函数在生成时并没有被创建,而发生调用后,才能被编译器检查到是否调用发生错误。
class P1 {
public:
void func1() {
cout << "P1" << endl;
}
};
class P2 {
public:
void func2() {
cout << "P2" << endl;
}
};
template <class T>
class TP {
public:
T obj;
void func3() {
obj.func1();
}
void func4() {
obj.func2();
}
};
int main() {
TP<P1> p1{};
p1.func3();
p1.func4();
system("pause");
return 0;
}
3.3类模板实例化的对象作为函数参数
a)指定传入的类型,直接显示对象的数据类型。
b)将对象中的参数变成模板传入。
c)整个类模板化,将对象类型模板化进行传递。
template <class Name_T,class Age_T = int>
class Person {
public:
Person(Name_T name, Age_T age) {
this->m_Name = name;
this->m_Age = age;
}
void showPerson() {
cout << "name:" << this->m_Name << " ";
cout << "age:" << this->m_Age << endl;
}
private:
Name_T m_Name;
Age_T m_Age;
};
//指定传入的类型,直接显示对象的数据类型。
void func1(Person<string, int>& p) {
p.showPerson();
}
//将对象中的参数变成模板传入。
template<class T1, class T2>
void func2(Person<T1, T2>& p) {
p.showPerson();
}
//整个类模板化,将对象类型模板化进行传递。
template<class T_>
void func3(T_& p) {
p.showPerson();
}
4、多类型模板
当需要返回的值的类型不确定时,可声明返回类型为auto(c++11),此时需要在函数体内使用decltype函数来获取返回值类型:
decltype(计算表达式) self_typename
示例:
auto Quick_func::get_max(T1& a, T2& b){
decltype(a > b ? a : b) out = a > b ? a : b;
cout << "out:" << out << endl;
return out;
}
int main()
{
int a = 20;
double b = 115.0;
typedef decltype(a > b ? a : b) outtype;
outtype out = get_max(a, b);
}
5、类模板与继承
a)如果父类是类模板,子类必须指定父类的参数类型。
b)如果想要同样灵活使用的父类的参数,将子类定义为类模板,指定子类的参数类型,将其作为继承到的父类参数类型即可。
示例:
template <class T>
class Base {
T num;
};
//指定子类的参数类型
class Son1 :public Base<float> {
};
//灵活使用模板参数
template<class T1,class T2>
class Son2 :public Base<T1> {
T2 son2_param;
};
6、类模板成员函数类外实现与分文件编写
6.1 定义
类外实现:
当类模板的成员函数在类内声明,类外进行实现时,在实现部分中,必须加上与类模板同样的template类型定义行,在普通类成员函数作用域的加上显示自定义类型使用,如:
template<class T>
void 类名<T>::函数名(T 形参)
{...}
分文件编写 :
由于类模板中的成员函数是在调用时才会生成,因此如果只include写有类模板成员函数声明的头文件会报错,无法链接到成员函数定义。
解决方法:
a)直接在调用该成员函数的文件中包含写有类模板成员函数定义的cpp文件。
b)将类模板成员函数定义和声明写在同一个文件中,并将文件后缀改为hpp。
6.2 示例
头文件:
template <class Name_T,class Age_T>
class Person {
public:
Person(Name_T name, Age_T age);
void showPerson();
private:
Name_T m_Name;
Age_T m_Age;
};
函数声明文件:
#include "head1.h"
template<class Name_T,class Age_T>
Person<Name_T, Age_T>::Person(Name_T name, Age_T age) {
this->m_Name = name;
this->m_Age = age;
}
template <class Name_T, class Age_T>
void Person<Name_T, Age_T>::showPerson() {
cout << "name:" << this->m_Name << " ";
cout << "age:" << this->m_Age << endl;
}
main函数文件:
#include "head1.h"
#include "func1.cpp"
int main() {
Person<string, int>* p = NULL;
p = new Person<string, int>("Adam", 23);
p->showPerson();
delete p;
system("pause");
return 0;
}
7、类模板友元
a)类模板中可以定义友元全局函数,从而直接使用类模板的自定义数据类型。
b)当只在类模板中声明友元全局函数时,由于编译顺序原因,需要把该友元全局函数定义在类模板的前面,才能让类模板中的友元声明实现,而与此同时,由于友元全局函数定义时,将类模板作为传入参数,需要在该函数定义前“声明”一下有该类模板的存在,否则不能完成函数定义。
template <class Name_T, class Age_T>
class Person;
template <class T1, class T2>
void printPerson2(Person<T1, T2>& p) {
cout << "name:" << p.m_Name << " ";
cout << "age:" << p.m_Age << endl;
}
template <class Name_T,class Age_T>
class Person {
//类内友元
friend void printPerson1(Person<Name_T, Age_T> &p) {
cout << "name:" << p.m_Name << " ";
cout << "age:" << p.m_Age << endl;
}
//类外实现友元
template <class T1, class T2>
friend void printPerson2(Person<T1, T2>& p);
public:
Person(Name_T name, Age_T age);
void showPerson();
private:
Name_T m_Name;
Age_T m_Age;
};