-
Notifications
You must be signed in to change notification settings - Fork 0
/
模板(概念, 函数模板(基础,注意事项,案列数组排序,普通函数与函数模板区别\调用规则),模板的局限性))
432 lines (408 loc) · 9.3 KB
/
模板(概念, 函数模板(基础,注意事项,案列数组排序,普通函数与函数模板区别\调用规则),模板的局限性))
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stdio.h>
#include<string>
using namespace std;
//本阶段主要针对C++泛型编程和STL技术做详细讲解,探讨C++更深层的使用
//模板的概念
//模板就是建立通用的模具,大大提高复用性
// 列如生活中模板, 如ppt模板和一寸照片模板等等......
// 上面的列子共有的特点是:不能直接使用,不是万能的
//
//特点
// 模板不可直接使用,它只是一个框架
// 模板并不是万能的
//函数模板
// c++中一个编程思维称为泛型编程,主要利用的技术就是模板
// c++提供两种模板机制:函数模板和类模板
//
// 函数模板语法
// 函数模板作用
// 建立一个通用函数,其函数返回类型和形参类型可以不具体定制,用一个虚拟的类型代表
//
// 语法
// template<typename T>
// template -- 声明
// typename -- 表明后面的符号是一种数据类型,可以用class代替
// T -- 通用的数据类型,名称可以替换,通常为大写字母
//
//总结:
// 函数模板利用关键子tempplate
// 使用函数模板有两种方式:自动推到\显示指定类型
// 模板的目的是为了提高复用性,将类型参数化
//
//两个整型交换的函数
//void swapInt(int& a, int& b)
//{
// int temp = a;
// a = b;
// b = temp;
//}
//
////交换两个浮点数的函数
//void swapDouble(double& a, double& b)
//{
// double temp = a;
// a = b;
// b = temp;
//}
//
////上面的交换函数,逻辑上不能说极其相似,只能说一模一样,用到了函数模板
////函数模板
//template<typename T>//声明一个模板,告诉编译器后面代码中紧跟这的t不要报错,t是一个通用的数据类型
//void mySwap(T &a, T &b)//使用的时候指定它
//{
// T temp = a;
// a = b;
// b = temp;
//}
//
//void test01()
//{
// int a = 10;
// int b = 20;
//
// //swapInt(a, b);
// //利用函数模板来交换
// //2中方式
// // 1.自动类型推到(传进去的是什么类型就是什么类型)
// // mySwap(a, b);
// // 2.显示指定类型()
// mySwap<int>(a, b);
// printf("a = %d\nb = %d\n", a, b);
//
// //double c = 1.1;
// //double d = 2.2;
// //printf("c = %f\nd = %f\n", c, d);
//}
//int main()
//{
// test01();
// system("pause");
// return 0;
//}
//注意事项
// 1.自动类型推到必须推到出一只的数据类型T,才可以使用
// 2.模板必须要确定T的数据类型,才可以使用
// 1.自动类型推到必须推到出一只的数据类型T,才可以使用
//template<class T>//typename可以替代成class,最好用class因为生内存
//void mySwab(T& a, T& b)
//{
// T temp = a;
// a = b;
// b = temp;
//}
//
//void test01()
//{
// int a = 10;
// int b = 20;
// char c = 'c';
//
// mySwab(a, b);//正确
// //mySwab(a, c); //错误,推到不出以值的T类型
//
// printf("a = %d\nb = %d\n", a, b);
//
//}
//
//// 2.模板必须要确定T的数据类型,才可以使用
//template<class T>
//void func()
//{
// cout << "func 调用" << endl;
//}
//
//void test02()
//{
// //func();//上面的模板没有编译器没有指定出来(也就是不能推到出来),必须手动(显示指定类型)
// func<int>();
//}
//
//int main()
//{
// //test01();
// test02();
// system("pause");
// return 0;
//}
//函数模板案列
//利用函数模板封装一个排序的函数,可以对不同数据类型进行排序
// 排序规则从大到小,排序算法为选择排序
// 分别利用char数组和int数组进行测试
//交换模板
//template<class T>
//void mySwap(T& a, T& b)
//{
// T temp = a;
// a = b;
// b = temp;
//
//}
//
////通用对数组排序的函数
//template <class T>
//void mySort(T& a, int len)
//{
// for (int i = 0; i < len; i++)
// {
// int max = i;//认定最大值
// for (int j = i+1; j < len; j++)
// {
// //认定的最大值 比 遍历出的数值 要下. 说明 j下标的元素才是真正的最大值
// if (a[max] < a[j])
// {
// max = j;//更新最大值下标
// }
// }
// if (max != i)
// {
// mySwap(a[max], a[i]);
// }
// }
//}
//
////求个数模板
//template<class T>
//void myPrint( T &a, int len)
//{
// cout << a << endl;//这个是不行的因为打印的是内存好像,反正不行
// cout << "虚假的" << endl;
// for (int i = 0; i < len; i++)
// {
// cout << a[i] << endl;
// }
//
//}
//
//void test01()
//{
// char b[] = "badcfe";
// int len = (sizeof(b) / sizeof(char));
// //int len = myLen(b);//个数模板不行
//
// mySort(b,len);//排序
// //printf("[%s]",b);
// myPrint(b,len);//打印
//}
//
//void test02()//同上测试
//{
// int a[] = { 1,3,5,2,4,6 };
// int len = (sizeof(a) / sizeof(int));
//
// mySort(a, len);
// myPrint(a, len);
//}
//
//int main()
//{
// test01();
// test02();
//
//
// return 0;
//}
//普通函数与函数模板的区别
//区别
// 普通函数调用时可以发生自动类型转换(隐式类型转换)
// 函数模板调用时,如果利用自动类型推到,不会发生隐士类型转换
// 如果利用显示指定类型的方式,可以发生隐式类型转换
//总结: 建议使用显示类型的方式,调用函数模板,因为可以自己确定通用性类型T
// 普通函数调用时可以发生自动类型转换(隐式类型转换)
//int myAdd01(int a, int b)
//{
// return a + b;
//}
//
//void test01()
//{
// int a = 10;
// int b = 20;
// char c = 'c';
//
// //myAdd01(a, b);
// cout << myAdd01(a,c) << endl;
//}
//// 函数模板调用时,如果利用自动类型推到,不会发生隐士类型转换
//template<class T>
//T myAdd02(T a, T b)
//{
// return a + b;
//}
//
//void test02()
//{
// int a = 10;
// int b = 20;
// char c = 'c';
//
// //自动推到
// //cout << myAdd02(a, c) << endl;//不能隐式转换
//
// //如果利用显示指定类型的方式,可以发生隐式类型转换
// //显示指定类型
// cout << myAdd02<int>(a, c) << endl;
//}
//
//int main()
//{
// test01();
//
// system("pause");
// return 0;
//}
//普通函数与函数模板的调用规则
//调用规则如下
// 如果函数模板和普通函数都可以实现,优先调用普通函数
// 可以通过空模板参数列表来强制调用函数模板
// 函数模板也可以发生重载
// 如果函数模板可以产生更好的匹配,优先调用函数模板
//总结: 既然提供了函数模板,最好就不要提供普通函数,否则出现二义性
// 如果函数模板和普通函数都可以实现,优先调用普通函数
//void myPrint(int a, int b)//就算是一个声明也是调用普通函数(仅仅在第一个规则中)
//{
// printf("调用的是普通函数\n");
//}
//
//template<class T>
//void myPrint(T a, T b)
//{
// printf("调用的是函数模板\n");
//}
//
//// 函数模板也可以发生重载
//template<class T>
//void myPrint(T a, T b,T c)
//{
// printf("调用重载的模板\n");
//}
//
//void test01()
//{
// int a = 10;
// int b = 20;
// int c = 30;
//
// //myPrint(a, b);
//
// // 可以通过空模板参数列表来强制调用函数模板
// //myPrint<>(a, b);
//
// // 函数模板也可以发生重载
// //myPrint(a, b, c);
//
// //如果函数模板可以产生更好的匹配,优先调用函数模板
// char c1 = 'a';
// char c2 = 'b';
//
// myPrint(c1, c1);
//}
//
//int main()
//{
// test01();
//
// system("pause");
// return 0;
//}
//模板的局限性
//局限性
// 模板的通用性不是万能的
// 列如
// template<class T>
// void f(T a, T b)
// {
// a = b;
// }
// 在上述代码中提供的赋值操作,如果传入的a和b十一额数组就无法实现了
//
// 在列如
// template<class T>
// void f(T a, T b)
// {
// if (a > b)
// {
// ......
// }
// }
// 在上述代码中,如果T的数据类型传入的是像person这样的自定义数据类型,也无法正常运行
// 因此C++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化的模板
//总结
// 利用具体化的模板,可以理解自定义类型的通用化
// 学习模板不是为了写模板,而是在STL能够运用系统提供的模板
//加一个person
class Person
{
public:
Person(string name, int age);
~Person();
//private:
string m_Name;
int m_Age;
};
Person::Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
Person::~Person()
{
}
//对比2个数是否相等函数
template<class T>
bool myCompare(T& a, T& b)
{
if (a == b)
{
return true;
}
return false;
}
//后goto2.直接写一个对应这个自定义类型的重载版本, 只要是这个自定义类型就走这段代码(第一个讲过也会,写第二个也就是这个)
//利用具体化的自定义数据类型的版本实现代码, 具体化优先调用
template<> bool myCompare(Person& p1, Person& p2)
{
if (p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age)
{
return true;
}
return false;
}
void test01()
{
int a = 10;
int b = 20;
bool ret = myCompare(a, b);
if (ret)
{
cout << "a == b" << endl;
}
else
{
cout << "a != b" << endl;
}
}
void test02()
{
Person p1("tom", 10);
Person p2("tom", 10);
bool ret = myCompare(p1, p2);
if (ret)
{
cout << "p1 == p2" << endl;
}
else
{
cout << "p1 != p2" << endl;
}
//对于自定义类型函数模板不会判断,有2种办法,1.函数重载(但是太麻烦了,因为要写一大堆,比如要==重载,>重载,<重载等等......)
//先goto2.直接写一个对应这个自定义类型的重载版本, 只要是这个自定义类型就走这段代码(第一个讲过也会,写第二个也就是这个)
}
int main()
{
//test01();
test02();
system("pause");
return 0;
}