C++的另一种编程思想成为泛型编程,主要利用的技术就是模板。
模板不可以直接使用。
模板分为函数模板和类模板:
一、函数模板
建立一个通用函数,其函数返回值类型和形参类型可以不具体确定,用一个虚拟的类型来代表。
模板目的:提高代码复用性,将类型参数化。
语法:
template<typename T>
函数声明或定义
template:声明创建模板
typename:表明其后面的符号是一种数据类型(可用class代替)
T:通用数据类型,名称不一定叫T ,通常是大写字母
//函数模板
template<typename T>//声明一个模板,告诉编译器后面代码中紧跟着的T是一个通用数据类型,不要报错
void mySwap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
void test() {
//使用函数模板,两种方式
//1、自动类型推导
int a = 10;
int b = 20;
mySwap(a, b);//传入的参数是int型,因此自动推导T为int型
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << endl;
//2、显示指定类型
mySwap<int>(a, b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
注意事项:
1、自动类型推导,必须推导出一致的数据类型T才可以使用。
意思是给T的数据的类型必须一致。
2、模板必须确定出T的数据类型才可以正常使用。
函数模板和普通函数的区别:
普通函数参数传入可以发生隐式类型转换,比如float转int,char转int等,
而函数模板使用自动类型推导时,不可以发生隐式转换;
使用显示指定类型时则可以发生隐式转换。
普通函数和函数模板的调用规则:
函数模板和普通函数同名时,
1、如果函数模板和普通函数都可以实现,优先调用普通函数
2、可以通过空模板参数列表来强制调用函数模板
eg:print<>(a,b);
3、函数模板也可以重载
4、如果函数模板可以产生更好的匹配,优先调用函数模板
更好的匹配:不发生隐式类型转换
模板的具体化解决自定义类型的通用化
template<class T>
bool compare(T& a, T& b) {
if (a == b) {
return true;
}
else {
return false;
}
}
class Person {
Person(int age, string name) {
this->m_age = age;
this->m_name = name;
}
int m_age;
string m_name;
};
int main() {
Person p1(10, "AA");
Person p2(10, "AA");
if (compare(p1, p2)) {//运行时报错,因为对象不能执行“==”操作
cout << "p1 == p2" << endl;
}
else
{
cout << "p1 != p2" << endl;
}
return 0;
}
加入模板具体化的代码就可以了:
template<> bool compare(Person& p1, Person& p2) {//模板具体化
if (p1.m_age == p2.m_age && p1.m_name == p2.m_name) {
return true;
}
else {
return false;
}
}
二、类模板
作用:建立一个通用类,类中的成员 数据类型可以不具体指定,用一个虚拟的类型表示。
语法:
template<typename T>
类
template:声明创建模板
typename:表明其后面的符号是一种数据类型(可用class代替)
T:通用数据类型,名称不一定叫T ,通常是大写字母
类模板实例:
template<class NameType,class AgeType>//<class NameType,class AgeType>是模板参数列表
class Person {
public:
Person(NameType name, AgeType age) {
this->m_name = name;
this->m_age = age;
}
void show() {
cout << "name = "<<this->m_name << "age = " << this->m_age << endl;
}
NameType m_name;
AgeType m_age;
};
void test() {
Person<string, int> p1("Tom", 111);//类模板只有显示推导
p1.show();
}
类模板没有自动类型推导的使用方式;
类模板在模板参数列表中可以有默认参数:
template<class NameType,class AgeType = int>//AgeType默认为int
//创建时
Person<string> p1("Tom", 111);
类模板中成员函数创建时机:调用时再创建
class Person1 {
public:
void showPerson1() {
cout << "person1 show" << endl;
}
};
class Person2 {
public:
void showPerson2() {
cout << "person1 show" << endl;
}
};
template<class T>
class myClass {
public:
T obj;//创建一个T类对象
void func1() {
obj.showPerson1();
}
void func2() {
obj.showPerson2();
}
};
void test() {
myClass<Person1> p1;
p1.func1();
p1.func2();// 运行会报错,但运行前不会报错,因为Person1类中没有showPerson2()这个成员函数,因此说明成员函数在调用时再创建
}
类模板对象做函数参数:
有三种传入方式:
1、指定传入的类型–直接显示对象的数据类型
2、参数模板化–将对象中的参数变为模板进行传递
3、整个类模板化–将这个对象类型 模板化进行传递
使用比较广泛的第一种传入方式。
template<class T1,class T2>
class Person {
public:
Person(T1 age, T2 name) {
this->m_age = age;
this->m_name = name;
}
void showPerson() {
cout << "age = " << this->m_age << " name = " << this->m_name << endl;
}
T1 m_age;
T2 m_name;
};
//1、指定传入的类型--直接显示对象的数据类型
//使用得最广泛
void showPerson1(Person<int,string>&p) {
p.showPerson();
}
void test1() {
Person<int, string>p1(1,"P1's name");
showPerson1(p1);//将类模板对象作为参数传入
}
//2、参数模板化--将对象中的参数变为模板进行传递
template<class T1,class T2>
void showPerson2(Person< T1, T2>&p) {
p.showPerson();
cout << "T1 的类型为:" << typeid(T1).name() << endl;
cout << "T2 的类型为:" << typeid(T2).name() << endl;
}
void test2() {
Person<int, string>p2(2, "P2's name");
showPerson2(p2);
}
//3、整个类模板化--将这个对象类型 模板化进行传递
template<class T>
void showPerson3(T &p) {
p.showPerson();
cout << "T 的类型为:" << typeid(T).name() << endl;
}
void test3() {
Person<int, string>p3(3, "P3's name");
showPerson3(p3);
}
类模板与继承
当子类继承的父类是类模板时,子类在声明时必须指出父类中T的类型。
如果要灵活指定父类中T的类型,子类也要变成类模板。
类模板成员函数类外实现
template<class T1,class T2>
class Person {
public:
//成员函数类内声明
Person(T1 name, T2 age);
void showPerson();
T1 m_name;
T2 m_age;
};
//成员函数类外实现
template<class T1,class T2>//要加上类模板参数列表
Person<T1, T2>::Person(T1 name, T2 age) {
this->m_name = name;
this->m_age = age;
}
template<class T1, class T2>
void Person<T1,T2>::showPerson() {
cout << "name = " << this->m_name << " age = " << this->m_age;
}
类模板的分文件编写问题以及解决
分文件编写一般是把#include、类声明,包括属性和成员函数的声明等放在头文件.h中。
类模板分文件复杂一些,将.h和.cpp的内容放在一起,把文件后缀名改为.hpp(约定叫.hpp)。