泛型模板
何为模板,模板,是指作图或设计方案的固定格式。模板是将一个事物的结构规律予以固定化、标准化的成果。 学习这部分知识,能够让我们快速学习STL的系统提供模板
# 函数模板
函数模板作用:建立通用的函数,可以不确定返回值类型和参数类型,而是用一个虚拟的类型表示
# 语法
template<typename T>
函数声明或定义
2
template
:声明创建模板typename
:一种数据类型,也可以写为class
T
:通用的数据类型,可以由其他字母代替,但通常大写字母
注意:
函数模板是一个蓝图,它本身并不是函数。是编译器产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。
在编译器编译阶段,对于函数模板的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如,当用int类型使用函数模板时,编译器通过对实参类型的推演,将T确定为int类型,然后产生一份专门处理int类型的代码,对于double类型也是如此。
# 使用
// 声明
template<typename T>
T function(T& t1, T& t2);
//定义
template<typename T>
T function(T& t1, T& t2) {
return t1 + t2;
}
//实例化
int ret = function<int>(12, 89); // 101
2
3
4
5
6
7
8
9
10
11
12
# 函数模板和普通函数
调用规则:
- 若函数模板和普通函数都可以调用,优先调用普通函数
- 想要强制调用函数模板,要通过空模板参数列表 如下例子的L15
void print(int a,int b) {
cout << "你好\n";
}
template<typename t>
void print(t a,t b) {
cout << "我好\n";
}
int main() {
int a, b;
print<>(a,b);
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 类模板
# 语法
template<class T>
类的声明或定义
2
template
: 声明创建模板class
: 一种数据类型,也可以写为typenameT
: 通用的数据类型,可以由其他字母代替,但通常大写字母
另外,类模板可以指定类型,如下:
点击查看
template<class NameType,class AgeType= int>
同样地,类模板在平时也需要分多文件编写:
template<class T>
class stu {
private:
int age;
string name;
public:
stu(int _age, string _name) : age(_age), name(_name) {};
void doHomework();
void playPhone();
};
2
3
4
5
6
7
8
9
10
11
#include"template.h"
template<class T>
void stu<T>::doHomework() {
cout << "写作业!" << endl;
}
template<class T>
void stu<T>::playPhone() {
cout << "玩手机!" << endl;
}
2
3
4
5
6
7
8
9
10
11
// Make sure to add code blocks to your code group
实例化:
#include "template.cpp"
using namespace std;
int main() {
stu<int> s(22, "zhaoliu");
s.doHomework();
s.playPhone();
return 0;
}
2
3
4
5
6
7
8
提示
要引入的是xxx.cpp
文件!
# 非类型模板参数
模板参数可分为类型形参和非类型形参
- 类型形参: 出现在模板参数列表中,跟在class或typename关键字之后的参数类型名称(class T)
- 非类型形参: 用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用(int N)
template<typename T, int N>
T add(T x,T y) {
int arr[N];
return x + y;
}
int main() {
cout << add<int,100>(100,200);
return 0;
}
2
3
4
5
6
7
8
9
10
注意:
- 非类型模板参数只允许使用整型家族,浮点数、类对象以及字符串是不允许作为非类型模板参数的
- 非类型的模板参数在编译期就需要确认结果,因为编译器在编译阶段就需要根据传入的非类型模板参数生成对应的类或函数
# 函数模板的特化
步骤:
- 首先必须要有一个基础的函数模板。
- 关键字template后面接一对空的尖括号<>。
- 函数名后跟一对尖括号,尖括号中指定需要特化的类型。
- 函数形参表必须要和模板函数的基础参数类型完全相同,否则不同的编译器可能会报一些奇怪的错误。
例如,我们想要编写一个程序判断两个同类型的数据是否相同,可以这样做:
template<typename T>
bool isEqual(T t1,T t2) {
return t1 == t2;
}
int main() {
cout << isEqual<int>(100,200); // 0
cout << isEqual<double>(3.14,3.14); // 1
}
2
3
4
5
6
7
8
但是当我们进行一下测试时,会发现错误,判断结果是这两个字符串不相等,这很好理解,因为我们希望的是该函数能够判断两个字符串的内容是否相等,而该函数实际上判断是确实这两个字符串所存储的地址是否相同,这是两个存在于栈区的字符串,其地址显然是不同的。
char ch1[] = "asdf";
char ch2[] = "asdf";
cout << isEqual<char*>(ch1,ch2); // 0
2
3
接下来,我们就要对这种情况进行特殊处理,即”特化“
#include<string.h>
// 函数原模版
template<typename T>
bool isEqual(T t1,T t2) {
return t1 == t2;
}
//函数模板的特化
template<>
bool isEqual(char* ch1,char* ch2) {
return strcmp(ch1,ch2) == 0;
}
int main() {
char ch1[] = "asdf";
char ch2[] = "asdf";
cout << isEqual<char*>(ch1,ch2); // 1
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 类模板的特化
类模板也可以针对特殊类型进行特殊化实现,并且类模板的特化又可分为全特化和偏特化
# 全特化
全特化就是将类模板的参数全部确定化
步骤:
- 首先必须要有一个基础的类模板。
- 关键字template后面接一对空的尖括号<>。
- 类名后跟一对尖括号,尖括号中指定需要特化的类型。
// 类原模版
template<class T1,class T2>
class A {
private:
T1 t1;
T2 t2;
public:
A(T1 _t1,T2 _t2) : t1(_t1),t2(_t2) {
cout << "A <T1, T2>" << endl;
};
};
// 类模板的特化
template<>
class A<int, double> {
private:
int t1;
double t2;
public:
A(int _t1,double _t2) : t1(_t1),t2(_t2) {
cout << "A <int, double>" << endl;
};
};
int main() {
A<int,string> a1(12,"wewf"); //A <T1, T2>
A<int,double> a2(12, 3.111); //A <int, double>
return 0;
}
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
# 偏特化
偏特化是指任何针对模板参数进一步进行条件限制设计的特化版本
// 类模板的偏特化
// 特化部分参数
template<class T2>
class A<int, T2> {
private:
int t1;
T2 t2;
public:
A(int _t1,T2 _t2) : t1(_t1),t2(_t2) {
cout << "A <int, T2>" << endl;
};
};
// 对参数进行限制
template<class T1,class T2>
class A<T1&, T2&> {
private:
T1 t1;
T2 t2;
public:
A(T1 _t1,T2 _t2) : t1(_t1),t2(_t2) {
cout << "A <T1&, T2&>" << endl;
};
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 总结
为了方便代码的查看和维护,我们会在项目工程中一般将函数或类的声明放在.h
文件中,将函数或类的实现放在.cpp
文件中。
值得注意的是,模板不宜使用编译分离这种思想,不论是函数模板还是类模板,将模板的声明和定义都放到一个文件当中就行了。
- 模板优点:
- 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生。
- 增强了代码的灵活性。
- 模板缺点:
- 模板会导致代码膨胀问题,也会导致编译时间变长。
- 出现模板编译错误时,错误信息非常凌乱,不易定位错误。
# from cen