一.概念
泛型编程:通俗的讲就是模版。用一种通用的模板来写程序。
模板:又分为函数模版和类模版,这篇博客主要说的是函数模版。
函数模版:实际上是建立一个和函数,函数的类型和函数的形参类型不具体指定,用虚拟的类型来代表,这个通用的函数就称为函数模版。
(注意:凡是函数体相同的函数都可以用这个模版来代替,不必定义多个函数,只需要在模版中定义一次即可,再调用函数时系统会自己根据实参的类型类取代实参中的具体类型
,从而实现了不同函数的功能)
二.函数模版的实现
首先看一段代码:功能呢是实现char类型和 int 类型的两个变量的交换
void myswap1(int &a, int &b)
{
int c = 0;
c = a;
a = b;
b = c;
}
void myswap2(char &a, char &b)
{
int c = 0;
c = a;
a = b;
b = c;
}
int main()
{
int a = 10;
int b = 20;
char c = 'c';
char d = 'd';
myswap1(a, b);
myswap2(c, d);
cout << "a=" << a << " b=" << b << endl;
cout << "c=" << c << " d=" << d << endl;
system("pause");
return 0;
}
上述代码虽然实现了功能,但是显的很麻烦,假如我在多加几个类型就要在定义同样的函数好几个.所以C++就提供了函数模版机制,让类型参数化,方便我们去编程。
首先看看泛型编程函数模版的语法用法:
接下来用代码继续解释:
template <typename T>
void myswap(T &a, T&b)
{
T c;
c = a;
a = b;
b = c;
}
int main()
{
int a = 10;
int b = 20;
char c = 'c';
char d = 'd';
//myswap<int>(a, b);//函数模版显示类型调用
//myswap(a, b);//自动类型推倒
myswap<char>(c, d);
myswap(c, d);
cout << "a=" << a << " b=" << b << endl;
cout << "c=" << c << " d=" << d << endl;
system("pause");
return 0;
}
强化应用:对不同类型的数组进行排序
1.int 类型
template <typename T1, typename T2>
void mysort(T1 *arr, T2 sz)
{
T2 i, j;
T1 tmp;
for (i = 0; i < sz - 1; i++)
{
for (j = 0; j < sz - 1 - i; j++)
{
if (arr[j]>arr[j + 1])
{
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
template <typename T1, typename T2>
void print(T1 *arr,T2 sz)
{
T2 i;
cout << "从小到大排序:";
for (i = 0; i < sz; i++){cout << arr[i] << " ";}cout << endl;}int main(){int arr[] = { 9,8,7,6,5,4,3,2,1};int sz = sizeof(arr) / sizeof(arr[0]);mysort<int, int >(arr, sz);print<int, int >(arr, sz);system("pause");return 0;}
char arr[] = {'f','e','d','c','b','a'};
int sz = sizeof(arr) / sizeof(arr[0]);
mysort<char, int >(arr, sz);
print<char, int >(arr, sz);
自己可以根据自己想测试的类型进行测试~
三.函数模版和函数和普通函数的区别
1.函数模版和函数重载
看如下代码:
template <typename T>
void myswap(T &a, T&b)
{
cout << "我是模版函数" << endl;
}
void myswap(int a, char b)
{
cout << "我是普通函数" << endl;
}
int main()
{
int a = 10;
char b = 'z';
myswap(a, b);
myswap(b, a);
myswap(a, a);
system("pause");
return 0;
}
为什么是这样:1.普通函数的调用,可以进行隐式的类型转换
2.函数模版的调用,不会进行隐式的转换。
2.数模版也可以像普通函数一样重载:
①C++编译器优先考虑普通函数
②如果函数模版可以产生一个更好的匹配那么选择函数模版
③可以通过空模版实参列表的语法限定编译器只通过模版匹配
看如下代码:
template <typename T>下面测试调用部分:
void myswap(T a, T b)
{
cout << "我是两个参数的模版函数" << endl;
}
template <typename T>
void myswap(T a, T b,T c)
{
cout << "我是三个参数的函数模版" << endl;
}
void myswap(int a, int b)
{
cout << "我是普通函数" << endl;
}
int main()结果如下,与上图分析完全符合
{
int a = 10;
int b = 20;
myswap(a, b);//因为函数模版函数和普通函数同时满足,所以系统优先选择普通函数
myswap<>(a, b);//如果想通过函数模版调用,加上<>(空模版参数列表),就可以调用函数模版函数
myswap(3.0, 4.0);//这个时候只有函数模板函数可以产生更好的匹配,所以调用函数模版函数
myswap(3.0, 4.0, 5.0);//函数模版函数重载
myswap('a', 10);//参数类型不同,而函模版要求参数类型相同,因为普通函数可以进行隐式转换,所以调用普通函数
system("pause");
return 0;
}
四.探究函数模版的机制
1.编译器并不是把函数模版处理成能够处理任意类型的函数
2.编译器从函数模版通过具体类型产生不同的函数
3.编译器会对函数模版进行两次编译:在声明的地方对函数模版代码进行编译,在调用的地方对参数替换后的代码进行编译.
第一次编译:在声明的地方对函数进行词法分析,语法分析,句法分析,产生一个简陋的函数模型。
第二次编译:根据你的调用,产生一个具体的函数原型,如果还有不同参数类型函数模版函数的调用,继续产生一个具体的函数原型。
汇编语言如下图: