深入理解模板

模板是泛型编程的实现方式之一,只需要写一份便可以套用不同类型的类、函数、变量。

(以下涉及的代码有部分是从https://blog.csdn.net/m0_64361907/article/details/126788130 处转载)

模板函数

这是普通的 c 语言写交换函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<iostream>
using namespace std;

void Swapi(int* a, int* b){
int tmp = *a;
*a = *b;
*b = tmp;
}

void Swapd(double* a, double* b){
double tmp = *a;
*a = *b;
*b = tmp;
}
//……
int main(){
int a = 1, b = 2;
Swapi(&a, &b);
double c = 1.1, d = 2.2;
Swapd(&c, &d);

return 0;
}

可以看到,对于不同的参数类型,我们要写多个不同的交换函数。

而在 c 语言中,是不支持函数重载的,所以不同的交换函数无法使用同样的函数名实现重载

c++写交换函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<iostream>
using namespace std;

void Swap(int& x, int& y){
int tmp = x;
x = y;
y = tmp;
}

void Swap(double& x, double& y){
double tmp = x;
x = y;
y = tmp;
}
//……
int main(){
int a = 1, b = 2;
Swap(a, b);

double c = 1.1, d = 2.2;
Swap(c, d);
return 0;
}

C++ 中有了引用和函数重载,但是在实现不同类型参数的交换函数时仍然很麻烦。

所以就有了模板

使用模板的交换函数

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 <class T>
void Swap(T& x, T& y){
T tmp = x;
x = y;
y = tmp;
}

int main(){
int a = 1, b = 2;
Swap(a, b);

double c = 1.1, d = 2.2;
Swap(c, d);
return 0;
}

如此,编译器能够自己套用对应的参数类型。

显示实例化和隐式实例化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<iostream>
using namespace std;

template <class T>
T Add(const T& x,const T& y){
return x + y;
}

int main(){
int a = 1, b = 2;
double c = 1.1, d = 2.2;

cout << Add(a, b) << endl;//编译器要自己推类型的是隐式实例化
cout << Add(c, d) << endl;

//cout << Add(a, c) << endl;//error这样的写法就错了,为难编译器了,编译器也推不出来了
cout << Add<int>(a, c) << endl;//不需要编译器去推的是显示实例化
cout << Add<double>(b, d) << endl;

cout << Add(a, (int)c) << endl;
return 0;
}

不告诉编译器参数类型的是隐式实例化

指定了参数类型的是显式实例化

类模板

此处将会以一个建议的 vector 容器的模板类实现作为例子。

实现 vector 落后的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef int VDateType;
class vector{
public:
//……
private:
VDateType* _a;
size_t _size;
size_t _capacity;
};

int main(){
vector v1;
vector v2;
return 0;
}

以前的实现方式是这样的,但我们不能让v1类型为 int,v2类型为 double。

使用类模板的方式

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include<iostream>
#include<assert.h>
using namespace std;

namespace kcc{
template<class T>
class vector{
public:
vector()
:_a(nullptr)
, _size(0)
, _capacity(0)
{}

// 拷贝构造和operator= 这里涉及深浅拷贝问题,还挺复杂,后面具体再讲

~vector(){
delete[] _a;
_a = nullptr;
_size = _capacity = 0;
}

void push_back(const T& x){
if (_size == _capacity){
int newcapacity = _capacity == 0 ? 4 : _capacity * 2;
T* tmp = new T[newcapacity];
if (_a){
memcpy(tmp, _a, sizeof(T) * _size);
delete[] _a;
}
_a = tmp;
_capacity = newcapacity;
}

_a[_size] = x;
++_size;
}

// 读+写
T& operator[](size_t pos);
size_t size();
private:
T* _a;
size_t _size;
size_t _capacity;
};

// 模板不支持分离编译,也就是声明在.h ,定义在.cpp,原因后面再讲
// 建议就是定义在一个文件 xxx.h xxx.hpp
// 在类外面定义
template<class T>
T& vector<T>::operator[](size_t pos){
assert(pos < _size);

return _a[pos];
}

template<class T>
size_t vector<T>::size(){
return _size;
}
}

int main(){
kcc::vector<int> v1; // int
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);

// v1.operator[](3);
//cout << v1[3] << endl;
//cout << v1[5] << endl;
for (size_t i = 0; i < v1.size(); ++i){
v1[i] *= 2;
}
cout << endl;

for (size_t i = 0; i < v1.size(); ++i){
cout << v1[i] << " ";
}
cout << endl;


kcc::vector<double> v2; // double
v2.push_back(1.1);
v2.push_back(2.2);
v2.push_back(3.3);
v2.push_back(4.4);

for (size_t i = 0; i < v2.size(); ++i){
cout << v2[i] << " ";
}
cout << endl;

return 0;
}

作者

Erial

发布于

2022-09-19

更新于

2023-02-19

许可协议

评论