概述
函数模板与函数重载
一,什么是函数重载
1,在理解什么是函数重载之前,先理解一下函数符号生成规则,在C语言中,函数符号的生成只与函数名相关,这个是什么意思呢?如下代码:
#include<stdio.h>
int sum(int a,int b){return a+b;}
double sum (double a,double b){return a+b;}
float sum (float a,float b){return a+b;}
这些代码在main.c文件下运行,会出现这样的报错:
main.c:error c2371: "sum"重定义。
(1)对于使用__cdecl调用约定的函数,在函数名称前加一下划线,不考虑参数和返回值。
例如上述三个函数: 都是_sum
(2)对于使用__fastcall调用约定的函数,在函数名称前后各加一@符号,后跟参数的长度,不考虑返回值。
例如 int __fastcall Test(int n)的修饰名称为@Test@4;
(3)对于使用 __stdcall 标准调用约定的函数,在函数名称前加一下划线,名称后加@符号,后跟参数的长度,不考虑返回值。
例如 int __stdcall Test(int n,int m)的修饰名称为 _Test@8
2,在C++中,函数符号的生成与函数名,函数的返回值,函数的形参列表,即函数原型有关,如在main.cpp中:
#include<iostream.h>
using namespace syd;
int sum(int a,int b){return a+b;}
double sum (double a,double b){return a+b;}
float sum (float a,float b){return a+b;}
生成的函数符号如格式:?函数名称@@YA返回值参数列表@Z/Z(@Z表示有形参,Z表示没有形参)
int 型: ?sum@@YAHHH@Z
double型: ?sum@@YANNN@Z
float型: ?sum@@YAMMM@Z
调用约定: YA _cdecl YG _stdcall YI _fastcall
返回值类型:bool _N int H double N float M
所以对于C++编译器来说,上述三个函数生成的函数符号完全不相同,即为三个不同的函数,
如:bool compare (int a, int b);来说,函数符号为:?compare@@_NHH@Z
从上面可以知道:函数重载的条件是:1,函数名同名;2,函数的参数不同;3,同作用域
(1)函数的参数顺序不同可以作为重载的条件;
(2)函数参数的个数不同可以作为重载的条件;
(3)函数的参数不同也可以作为重载的条件;
调用约定
_cdecl c语言标准调用约定
_stdcall windows标准调用约定
_fastcall 快速调用约定
_thiscall 类成员方法的调用约定
对于形参的开辟和清理:
cdecl 由调用方开辟,由调用方清理;
stdcall 由调用方开辟,被调用方清理;
fastcall 前两个参数不开辟内存,之后的参数由调用方开辟,被调用方清理
二,模板函数的重载
1,什么是函数模板?
为什么要讲函数模板呢,因为对于C++来说,函数虽然允许重载,但是如果要实现任意两个数之和这种功能,如果用函数重载的话,需要写很多种重载函数,十分不方便,这个时候需要一个能够统一的智能函数,不管是什么类型,都能实现两数之和的这个函数功能,这个时候就需要函数模板。
函数模板允许程序员编写一个单独的函数定义,以处理许多不同的数据类型,而不必为每个使用的数据类型编写单独的函数。函数模板不是实际的函数,而是编译器用于生成一个或多个函数的 “模具”。在编写函数模板时,不必为形参、返回值或局部变量指定实际类型,而是使用类型形参来指定通用数据类型。当编译器遇到对函数的调用时,它将检查其实参的数据类型,并生成将与这些数据类型配合使用的函数代码。
template<模板参数表>
返回类型 函数名(形式参数表)
{
……;
}//函数体
template为函数模板的关键字,如求两数之和函数模板为:
template<typename group>
group Sum(group a, group b)
{
return a+b;
}
int main()
{
int a = 10;
int b = 20;
double c = 2.1;
double d = 2.8;
cout << Sum(a, b) << endl;
cout << Sum(c, d) << endl;
return 0;
}
什么是函数模板,什么是模板函数?template group Sum(group a, group b){return a+b;}是函数模板,其 实例化之后的函数Sum 是模板函数,这个实例化过程不是宏替换,而是重命名,即typedef int Group
模板函数的推演分为隐式推演和显式推演:
隐式推演就是编译器推演,显式推演是程序猿指定好这个函数采用何种参数:如
Sum<int>(a,b); Sum<double>(a,b);
程序员为这个函数指定参数类型。
2,什么是函数模板的重载?
#include<iostream>
using namespace std;
template <class T>
void fun(T a, T b)
{
}
template <class T>
void fun(T a)
{
}
int main ()
{
fun(11,22);
fun(23);
return 0;
}
这就是模板函数的重载,fun如果是两个参数,那么就实例化fun(T a, T b),如果是一个参数,那么就实例化fun(T a)。
template<class T> void fun (T a) {}
template<class U> void fun (U a) {}
那么这两个算模板函数的重载吗?
#include<iostream>
using namespace std;
template <class U>
void fun( U b)
{
}
template <class T>
void fun(T a)
{
}
int main()
{
fun(12);
return 0;
}
系统会出现这样的报错:
1>------ 已启动生成: 项目: C++上课代码集合, 配置: Debug Win32 ------
1>函数重载.cpp
1>C:UsersccayjsourcereposC++上课代码集合函数重载.cpp(10,6): error C2995: “void fun(U)”: 函数 模板已经定义
1>C:UsersccayjsourcereposC++上课代码集合函数重载.cpp(5): message : 参见“fun”的声明
1>C:UsersccayjsourcereposC++上课代码集合函数重载.cpp(16,2): error C2065: “fun”: 未声明的标识符
1>已完成生成项目“C++上课代码集合.vcxproj”的操作 - 失败。
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========
显而易见,上述代码不算函数模板重载,那么是怎么个原因呢?
在函数模板推演的过程中,typedef int T void fun (T a){} 和 typedef int U void fun (U a),都被推演为整形,对于编译器来说,这两个重定义了,所以不能函数重载,我们不能拿函数模板名称不同还辨别函数重载。
下面两个模板函数算重载吗?
#include<iostream>
using namespace std;
template <class T>
void fun( T b)
{
}
template <class T>
void fun(T a, int x)
{
}
int main()
{
fun(12);
fun(12, 23);
return 0;
}
1>------ 已启动生成: 项目: C++上课代码集合, 配置: Debug Win32 ------
1>函数重载.cpp
1>C++上课代码集合.vcxproj -> C:UsersccayjsourcereposC++上课代码集合DebugC++上课代码集合.exe
========== 生成: 成功 1 个,失败 0 个,最新 0 个,跳过 0 个 ==========
这个就是模板函数的重载,编辑器在编译的时候,因为形参数量不同,在推演之后生成的函数符号也不相同,构成重载。
3,模板函数和宏替换有什么区别?
#include<iostream>
using namespace std;
template <class T>
void fun( T b)
{
T x, y; //TX =>
//TY =>
}
int main()
{
int a = 10;
int* p = &a;
fun(a); //TA =>
fun(p); //TP =>
return 0;
}
那么TA,TP,fun§ 调用的TX,TY应该被推演成什么类型?
我们应该很容易的就能推演出:TA => int ; TP => int*
那么TX,TY应该被推演为什么?
TX => int* ; TY => int ;
这个结果对还是不对,我们在刚才已经说了,这个实例化过程不是宏替换,那么这个结果是对还是不对?
其实是不对的,正确的应该是:
TX => int* ; TY => int* ;
那fun(a)对于的TX,TY应该是什么类型,都是int型;
typedef int T
void fun (T a)
{
T x,y; //x和y的类型都是int
}
typedef int* T
void fun (T a)
{
T x,y;//x和y的类型都是int*
}
宏替换和模板函数采用的重命名不同:
#define PINT int*
typedef int* SINT;
int main()
{
PINT x, y;
SINT a, b;
}
x和y类型一样吗?不一样,x是int*,y是int类型,这是宏替换;
a和b类型都是int*;
可以使用头文件typeinfo中的typeid(x).name 检验类型;
#include<iostream>
using namespace std;
template<class T>
void fun(T* a)
{
T x, y;
//这个T是什么类型,x是什么类型,y是什么类型?
}
int main()
{
int a = 10;
int* p = &a;
fun(p);
return 0;
}
为什么T是整型而不是指针?
根本原因是因为:我们在定义指针是,我们的*是和变量名结合,所以在我们推演的时候,推演成为了typedef int T;
所以我们的T,x,y都是int型;
#include<iostream>
#include<typeinfo>
using namespace std;
template<class T> //一般的模板函数
void fun(T a)
{
}
template<class T> //部分特化的模板函数
void fun(T *a)
{
}
void fun(char *p) //完全特化的模板函数
{
}
int main()
{
int a = 10;
int* p = &a;
fun(a);
fun(p);
return 0;
}
//这个概念是什么概念呢?
//为什么可以编译通过呢?
//这三个函数有什么差别呢?
为什么可以编译通过呢?
从函数重载来说,这三个函数推演之后,生成的函数符号不同,所以可以重载,void fun(T a), 只要是个类型我都可以接受,class 、int、double、char都行,我都可以推演;但是 void fun(T *a),我只能接受指针类型的参数;void fun(char *a),只能接受char类型的指针。
from 西安图论教育杨和平老师
最后
以上就是贪玩抽屉为你收集整理的函数模板与函数重载函数模板与函数重载的全部内容,希望文章能够帮你解决函数模板与函数重载函数模板与函数重载所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复