# 一、函数模板
模板就是建立通用模具,大大提高复用性
- C++有一种编程思想为泛型编程,主要利用的技术就是模板
- C++提供了两种模版机制:
函数模板
,类模板
# 1.1模板语法
- 作用:建立一个通用的模板,其函数的返回值和形参类型可以不具体制定,用一个虚拟的类型来代表
- 语法:
template<typename T>
解释:
- template表示声明创建模板
- typename表示其后面的符号是一种数据类型,可以用class代替
- T通用的数据类型,名称可以代替,通常是大写字母
#include <iostream>
using namespace std;
// 交换两个整型
void swapInt(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
// 交换两个浮点
void swapFloat(float &a, float &b) {
float temp = a;
a = b;
b = temp;
}
// 泛型编程(函数模板)
template <typename T>
void mySwap(T &a, T &b) {
T temp = a;
a = b;
b = temp;
}
int main() {
int a = 10, b = 20;
swapInt(a, b);
cout << "swapInt a = " << a << endl;
cout << "swapInt b = " << b << endl;
// 利用函数模板进行交换
// 两种方式使用函数模板
// 1、自动类型推导
mySwap(a, b);
cout << "mySwap a = " << a << endl;
cout << "mySwap b = " << b << endl;
// 2、显示指定类型(此时通过尖括号告诉编译器T在这里指的是int)
mySwap<int>(a, b);
cout << "mySwap a = " << a << endl;
cout << "mySwap b = " << b << endl;
}
# 1.2函数模板注意事项
注意事项:
- 自动类型推倒,必须推导出一致的数据类型T,才可以使用
- 模板必须要确定T的数据类型,才可以使用
# 1.3普通函数与函数模板的区别
- 普通函数在调用时可以发生自动类型转换(隐式类型转换)
- 函数模板调用时,如果利用自动类型推倒,不会发生隐式类型转换
- 如果利用显示指定类型的方式,可以发生隐式类型转换
# 1.4普通函数与函数模板调用规则
- 如果函数模板和普通函数都可以实现,优先调用普通函数
- 可以通过空模板参数列表来强制调用函数模板
- 函数模板也可以发生重载
- 如果函数模板可以产生更好的匹配,优先调用函数模板
# 1.5模板的局限性
模板并不是万能的,有些特定数据类型,需要具体化方式进行特殊实现
例如:在进行比较大小时,如果传入的数据将无法进行比较操作
#include <iostream>
#include "string"
using namespace std;
class Person {
public:
string m_Name;
int m_Age;
Person(): m_Name("Jack"), m_Age(20) {}
};
// 对比两个数是否相等
template<class T>
bool myCompare(T &a, T &b) {
if (a == b) {
return true;
} else {
return false;
}
}
// 利用具体化Person的版本实现代码,具体化优先调用
template<> bool myCompare(Person &p1, Person &p2) {
if (p1.m_Age == p2.m_Age && p1.m_Name == p2.m_Name) {
return true;
} else {
return false;
}
}
int main() {
int a = 100, b = 100;
bool ret_01 = myCompare(a, b);
cout << ret_01 << endl;
Person p1;
Person p2;
bool ret_02 = myCompare(p1, p2);
cout << ret_02 << endl;
return 0;
}
总结:
- 利用具体化的模板,可以解决自定义类型的通用化
- 学习模板并不是为了写模板,而是在STL能够运用系统提供的模板
# 二、类模板
# 2.1类模板语法
类模板的作用是建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表
- 语法:
template<typename T>
解释:template表示声明创建模板,typename表示其后面的符号是一种数据类型,可以用class代替,T表示通用数据类型,名称可以代替,通常使用大写字母
#include <iostream>
#include "string"
using namespace std;
// 类模板
template<class NameType, class AgeType>
class Person {
public:
NameType m_Name;
AgeType m_Age;
Person(NameType name, AgeType age) {
this->m_Name = name;
this->m_Age = age;
}
};
int main() {
Person<string, int> person("Jack", 18);
cout << person.m_Name << endl;
cout << person.m_Age << endl;
return 0;
}
# 2.2类模板与函数模板
主要区别有两点:
- 类模板没有自动类型推导的使用方式
- 类模板在模板参数列表中可以有默认参数
类模板的默认参数:
//
// Created by Jacob Lee on 2021/12/27.
//
#include "iostream"
#include "string"
using namespace std;
template<class NameType, class AgeType>
class Person{
public:
NameType m_Name;
AgeType m_Age;
Person(NameType name, AgeType age) {
this->m_Name = name;
this->m_Age = age;
}
void showPerson() {
cout << "name:" << this->m_Name << endl;
cout << "age:" << this->m_Age << endl;
}
};
int main() {
Person<string, int> person("Jack", 20);
person.showPerson();
return 0;
}
# 2.3类模板中成员函数创建时机
类模板中成员函数和普通类中成员函数创建时机是有所区别的
- 普通类中的成员函数一开始就可以创建
- 类模板中的成员函数在调用时才可以创建
# 2.4类模板对象做函数参数
一共有三种传入方式:
- 制定传入的类型:直接显示对象的数据类型
- 参数模板化:将对象中的参数变为模板进行传递
- 整个类模板化:将这个对象类型模板化进行传递
#include "iostream"
#include "string"
using namespace std;
template<class T1, class T2>
class Person {
public:
T1 m_Name;
T2 m_Age;
Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}
void showPerson() {
cout << "name:" << this->m_Name << endl;
cout << "age:" << this->m_Age << endl;
}
};
// 1、制定传入类型
void showMethod01(Person<string, int> &person) {
person.showPerson();
}
// 2、参数模板化
template<class T1, class T2>
void showMethod02(Person<T1, T2> &person) {
person.showPerson();
}
// 3、对象类型模板化
template<class T>
void showMethod03(T &person) {
person.showPerson();
}
int main() {
Person<string, int> p1("Lee", 18);
showMethod01(p1);
showMethod02(p1);
showMethod03(p1);
}
# 2.5类模板与继承
当类模板碰到继承时,需要注意以下几点:
- 当子类继承的父类是一个类模板时,子类在声明的时候需要指出父类中T的类型
- 当不指定时,编译器无法给子类分配内存
- 如果想灵活指定父类中T的类型,子类也需要是一个类模板
#include "iostream"
#include "string"
using namespace std;
template<class T>
class Base {
public:
T m;
};
class Son: public Base<string> {};
// 如果想要灵活的指定父类中T的类型,子类需要是一个类模板
template<class T1, class T2>
class Another: public Base<T1> {
public:
T2 n;
};
int main() {
Son son;
son.m = "1";
cout << son.m << endl;
Another<string, int> another;
another.m = "hello";
another.n = 20;
cout << another.m << endl;
cout << another.n << endl;
}
# 2.6类模板成员函数的类外实现
#include "iostream"
#include "string"
using namespace std;
template<class T1, class T2>
class Person {
public:
T1 m_Name;
T2 m_Age;
Person(T1 name, T2 age);
void showPerson();
};
// 类模板构造函数类外实现
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 << endl;
cout << "age:" << this->m_Age << endl;
}
int main() {
Person<string, int> person("Jack", 18);
person.showPerson();
return 0;
}
- 总结:类模板中的成员函数类外实现时,需要加上模板的参数列表
# 2.7类模板分文件编写
解决办法:
- 直接包含.cpp源文件
- 将声明和实现写到同一个文件中,并更改后缀名为.hpp,其中hpp是约定的名字并不是强制要求
# 2.8类模板与友元
全局函数类内的实现:直接在类内声明友元即可
全局函数类外的实现:需要提前让编译器值到全局函数的存在
建议全局函数做类内实现,用法简单且编译器可以直接识别