模板与泛型编程
模板
概念:建立通用模具,大大提高复用性,利用模板进行编程的思想被称为泛型编程
机制:函数模板与类模板
函数模板
建立一个通用函数,返回值类型与形参类型可以不具体指定,用虚拟类型代表
语法
1
2
template<typename T>
/*函数声明或定义*/- template:声明创建模板
- typename:表示后面的符号是一种数据类型
- T:通用数据类型,名称可以替换,但通常是大写字母
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<iostream>
using namespace std;
template<typename T> // 声明模板
void swapt(T &a, T &b){
T temp = a;
a = b;
b = temp;
}
int main(){
int a = 20;
int b = 10;
// 方法一:自动类型推导
swapt(a,b);
// 方法二:显式指定类型
swapt<int>(a,b);
cout << a << " " << b;
}注意
自动类型推导时,必须推导出一致的数据类型T才能使用
1
2
3int a; char c; swapt(a,c); // 出错模板必须要确定出T的数据类型才能用
1
2
3
4
5template<typename T> void f(){ return ; } f(); // 报错typename可以用class替换(class可以声明函数模板和类模板)
与普通函数的区别
- 普通函数调用可以发生隐式类型转换
- 函数模板使用自动类型推导时不可以发生隐式类型转换
- 使用显式指定类型时可以发生隐式类型转换
调用规则
- 普通函数与函数模板都可以实现时,优先调用普通函数
- 可以通过空模板参数列表来强制调用函数模板
- 函数模板也可以发生重载
- 如果函数模板可以产生更好的匹配,则优先调用函数模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include<iostream>
using namespace std;
void print(int a, int b){
cout <<"普通函数调用"<<endl;
cout << a <<" "<< b << endl;
}
template<typename T>
void print(T a, T b){
cout <<"函数模板调用"<<endl;
cout << a <<" "<< b << endl;
}
template<typename T>
void print(T a, T b, T c){
cout <<"重载函数模板调用"<<endl;
cout << a <<" "<< b <<" "<<c << endl;
}
int main(){
int a = 10;
int b = 10;
print(a,b);
// 更好匹配
char c = '1';
char d = '2';
print(c,d);
// 强制调用
print<>(a,b);
//重载调用
int e = 10;
print(a, b, e);
}类模板
建立一个通用类,类中的成员数据类型可以用虚拟类型代表
语法
1
2
template<typename T>
/*类*/- template:声明创建模板
- typename:表示后面的符号是一种数据类型
- T:通用数据类型,名称可以替换,但通常是大写字母
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include<iostream>
using namespace std;
template<class NameType, class AgeType>
class person{
public:
person(NameType name, AgeType age){
this->age = age;
this->name = name;
}
void show(){
cout <<"name: "<<this->name<<endl;
cout <<"age: "<<this->age<<endl;
}
NameType name;
AgeType age;
};
int main(){
// 使用模板参数列表指定类型
person<string, int> p1("Jack", 18);
p1.show();
}与函数模板的区别
类模板没有自动类型推导的方式
不能写
person p("a", 1);这种写法,只能显式指定类模板在模板参数列表中可以有默认参数
template<class NameType, class AgeType = int>可以指定默认的数据类型
创建时机
- 普通类中的成员函数一开始就可以创建
- 类模板中的成员函数在调用时才创建,因为调用时才能确定数据类型
类模板对象做函数参数
传入方式:
- 指定传入类型:直接显示对象的数据类型
- 参数模板化:将对象中的参数变为模板进行传递
- 整个类模板化:将这个对象类型模板化进行传递
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include<iostream>
using namespace std;
template<class NameType, class AgeType>
class person{
public:
person(NameType name, AgeType age){
this->age = age;
this->name = name;
}
void show(){
cout <<"name: "<<this->name<<endl;
cout <<"age: "<<this->age<<endl;
}
NameType name;
AgeType age;
};
// 指定传入类型
void printp1(person<string, int> &p){
p.show();
}
// 参数模板化
template<class T1, class T2>
void printp2(person<T1, T2> &p){
cout <<"T1: "<<typeid(T1).name()<<endl;
cout <<"T2: "<<typeid(T2).name()<<endl;
p.show();
}
// 整个类模板化
template<class T>
void printp3(T &p){
cout <<"T: "<<typeid(T).name()<<endl;
p.show();
}
int main(){
person<string, int> p("Jack", 18);
printp1(p);
printp2(p);
printp3(p);
}类模板与继承
当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
1
2
3
4
5
6
7template<class T> class base{ T m; }; class son:public base<int>{ };若想灵活指定父类中的T类型,子类也需要变成类模板
1
2
3
4
5
6
7
8
9
10
11template<class T> class base{ T m; }; template<class T1, class T2> class son:public base<T2>{ T1 obj; }; int main(){ son<int, char> s1; // 此时int为T1对应的obj的类型,char为T2对应父类中T的类型 }
模板特化
有些特定数据类型需要一些特定的实现方式,需要具体化实现
函数模板特化
在需要特化的函数前添加template<>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include<iostream>
using namespace std;
class person{
public:
person(string name, int age){
this->age = age;
this->name = name;
}
string name;
int age;
};
template<typename T>
bool cmp(T &a, T &b){
if (a==b){
return true;
}
else{
return false;
}
}
// 使用具体化person的版本实现代码,具体化优先调用
template<> bool cmp(person &p1, person &p2){
if (p1.name==p2.name&&p1.age ==p2.age) return true;
else return false;
}
int main(){
person p1("Jack", 10);
person p2("Jack", 10);
if (cmp(p1,p2)){
cout <<"a==b"<<endl;
}
else {
cout <<"a!=b"<<endl;
}
}类模板成员函数类外实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include<iostream>
using namespace std;
template<class NameType, class AgeType>
class person{
public:
person(NameType name, AgeType age);
void show();
NameType name;
AgeType age;
};
// 类外实现
template<class NameType, class AgeType>
person<NameType, AgeType>::person(NameType name, AgeType age){
this->age = age;
this->name = name;
}
template<class NameType, class AgeType>
void person<NameType, AgeType>::show(){
cout <<"name: " <<this->name << endl;
cout <<"age: "<<this->age<<endl;
}
int main(){
person<string, int> p("Jack", 18);
p.show();
}类模板分文件编写
由于类模板中成员函数在调用时才创建,这导致了分文件编写时链接不到,有两个方法
直接引入.cpp文件
将声明和实现写在同一个文件中,改后缀名为.hpp
常用第二种
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#pragma once
#include<iostream>
using namespace std;
template<class NameType, class AgeType>
class person{
public:
person(NameType name, AgeType age);
void show();
NameType name;
AgeType age;
};
// 类外实现
template<class NameType, class AgeType>
person<NameType, AgeType>::person(NameType name, AgeType age){
this->age = age;
this->name = name;
}
template<class NameType, class AgeType>
void person<NameType, AgeType>::show(){
cout <<"name: " <<this->name << endl;
cout <<"age: "<<this->age<<endl;
}(hpp文件)
1
2
3
4
5
6
7
8
9
#include<iostream>
using namespace std;
#include "person.hpp"
int main(){
person<string, int> p("Jack", 18);
p.show();
}(调用文件)
类模板与友元
使用场景
- 全局函数类内实现:直接类内声明友元
- 全局函数类外实现:需要提前让编译器知道全局函数的存在
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include<iostream>
using namespace std;
// 前向声明 类与模板函数
template<class NameType, class AgeType>
class person;
template<class NameType, class AgeType>
void printPerson2(person<NameType, AgeType> p);
template<class NameType, class AgeType>
class person{
// 类内实现
friend void printPerson(person<NameType, AgeType> p){
cout <<"name: " <<p.name << endl;
cout <<"age: "<<p.age<<endl;
}
// 类外实现 需要让编译器提前知道这个函数的存在
friend void printPerson2<>(person<NameType, AgeType> p);
public:
person(NameType name, AgeType age){
this->age = age;
this->name = name;
}
private:
NameType name;
AgeType age;
};
// 类外实现
template<class NameType, class AgeType>
void printPerson2(person<NameType, AgeType> p){
cout << "name: " << p.name << endl;
cout << "age: " << p.age << endl;
}
int main(){
person<string, int> p("Jack", 18);
printPerson(p);
printPerson2(p);
}