模板

如何实现一个通用的函数呢?(如针对不同的参数类型均可)

在平时的编程中,如何实现一个通用的函数呢?

首先想到的就是使用函数重载,但是这样一来会有几个不好的地方:

  1. 重载的函数仅仅只是类型不同,代码的复用率比较低,只要有新类型出现时就需要增加对应的函数
  2. 代码可维护率比较低,一个函数出错就有可能所有的重载都出错

那么有没有一种方式,使得编译器可以根据不同的类型来利用该模子来生成代码呢?

答案是有的——泛型编程,也就是一种编写与类型无关,与使用场景无关的通用代码,使得代码可以复用的手段,而模板则是泛型编程的基础

模板:

模板是C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。

  1. 模板是一种对类型进行参数化的工具;
  2. 通常有两种形式:函数模板类模板
  3. 函数模板针对仅参数类型不同的函数
  4. 类模板针对仅数据成员成员函数类型不同的类。

  使用模板的目的就是能够让程序员编写与类型无关的代码。比如编写了一个交换两个整型int 类型的swap函数,这个函数就只能实现int 型,对double,字符这些类型无法实现,要实现这些类型的交换就要重新编写另一个swap函数。使用模板的目的就是要让这程序的实现与类型无关,比如一个swap模板函数,即可以实现int 型,又可以实现double型的交换。模板可以应用于函数和类。下面分别介绍。

  注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。

函数模板

是一个函数家族,与类型无关,使用时才被参数化,根据实参类型产生函数的适用版本

函数模板格式

template //模板参数列表 可以使用template ,不可以使用struct

T1 _Add(T1 left,T2 right) //函数模板

{

​ return left + right;

}

实例化:
隐式实例化:

编译器根据实参推演模板参数的实际类型

显示实例化:

函数名和后加<>指定参数实际类型

匹配原则:

1.优先调用自己写的模板函数

2.模板函数不允许自动类型转换,普通函数可以自动类型转换

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
#pragma once
#include<assert.h>
#include<iostream>
using namespace std;


//不是一个真正的函数
template<class T> //模板参数列表 可以使用template<typename T> ,不可以使用struct
T _Add(T left,T right) //函数模板
{
return left + right;
}

template<class T>
void PrintfArray(T array,int size) //数组会自己转换类型
{
cout<<typeid(array).name() <<endl;
int i = 0;
for(i=0;i<size;i++)
{
cout<< array[i] << " ";
cout<<endl;
}

}

int main()
{
int array[] = {1,2,3,4,};
char str[] = "hello";
PrintfArray(array,sizeof(array)/sizeof(int));
PrintfArray(str,strlen(str));
//隐式实例化---不会进行隐式的类型转化,需要用户自己来强转/
cout<< _Add(1,2) <<endl; //根据实参类型来进行类型推演生成处理具体类型的函数
cout<< _Add(1,(int)2.0) <<endl; //但是面对参数为不同类型时无法判断如何输出,需要对参数进行处理,如强转类型

//显式实例化
_Add<int>(1,2.2);
_Add<>(1,2); //隐式实例化
return 0;
}

用模板写list类

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
template<class T>
class Seqlist
{
public:
Seqlist(int capacity = 10)
:_Data(new T[capacity])
,_capacity(capacity)
,_size(0)
{}

//拷贝构造:Seqlist(const Seqlist<T>& s);
//赋值运算符重载:Seqlist<T>& operator = (const Seqlist<T>& s)

void PushBack(const T& data)
{
_CheckCapacity();
_Data[_size++] = data;
}

void PopBack()
{
if(_size != 0)
--_size;
}

//访问任意位置
T& operator[](size_t index) //可能会修改s[i]中的元素
{
assert(index < _size);
return _Data[index];
}

//因为const类型的对象不能调用普通函数,这里是const对象访问
const T& operator[](size_t index) const
{
assert(index < _size);
return _Data[index];
}

//访问第一个元素
T& Front()
{
return _Data[0];
}

const T& Front()const
{
return _Data[0];
}

//访问最后一个元素
T& Back()
{
return _Data[_size-1];
}

const T& Back()const
{
return _Data[_size-1];
}

size_t Size() const
{
return _size;
}

size_t Capacity() const
{
return _capacity;
}

void Clear()
{
_size = 0;
}

~Seqlist();
private:
void _CheckCapacity()
{
if(_size == _capacity)
{
size_t newCapacity = (_capacity<<1);
//申请空间
T* Temp = new T[newCapacity];
//拷贝元素
memcpy(Temp,_Data,_size*sizeof(T));
//释放旧空间
delete[] _Data;
//替换新空间
_Data = Temp;
_capacity = newCapacity;
}
}
private:
T* _Data;
size_t _capacity;
size_t _size;
};

template<class T>
Seqlist<T>::~Seqlist()
{
if(_Data)
{
delete[] _Data;
_Data = nullptr;
_capacity = 0;
_size = 0;
}
}

void TestSeqlist()
{
Seqlist<int> s1;
Seqlist<double> s2;
Seqlist<char> s3;

s1.PushBack(1);
s1.PushBack(2);
s1.PushBack(3);
s1.PushBack(4);
s1[0] = 6;
cout<< s1.Size()<<endl;
cout<< s1.Front()<<endl;
cout<< s1.Back()<<endl;
s1.Clear();
cout<<s1.Size()<<endl;
}

#include<string>
void Test()
{
Seqlist<string> s;
s.PushBack("0000");
s.PushBack("1111");
s.PushBack("2222");
s.PushBack("3333");
s.PushBack("4444");
s.PushBack("5555");
s.PushBack("6666");
s.PushBack("7777");
s.PushBack("8888");
s.PushBack("9999");
}
int main()
{
//TestSeqlist();
Test();
return 0;
}
0%