2022.5.19 C++——缺省参数和函数重载

23 篇文章 0 订阅

1. 缺省参数

1.1缺省参数函数调用

(1) 一般情况下,函数调用时的实参个数应与形参相同,但为了更方便地使用函数,C++也允许定义具有缺省参数的函数,这种函数调用时,实参个数可以与形参不相同。
(2)缺省参数指在定义函数时为形参指定缺省值(默认值)。
(3)这样的函数在调用时,对于缺省参数,可以给出实参值,也可以不给出参数值。如果给出实参,将实参传递给形参进行调用,如果不给出实参,则按照缺省值进行调用。
缺省参数的函数调用:
缺省实参并不一定是常量表达式,可以是任意表达式,甚至可以通过函数调用给出。如果缺省实参是任意表达式,则函数每次被调用时该表达式被重新求值,但表达式必须有意义:
例 :

void fun(int a, int b = 0, int c = 100)
{
	cout << " a = " << a << " b = " << b << " c = " << c << endl;
}
int  main()
{
	fun(10);//1
	fun(10, 20);//2
	fun(10, 20, 30);//3
	fun(10, , 30);//4   error
	return 0;
}

分析:先看fun函数,fun函数中,形参个数有3个分别为a,b,c,但是在定义函数时给了b和c的值,如果下面调用这个fun函数的话,要么实参只给a给值,要么给a和b一起给值,要么给a,b,c一起给值,切记不要跨越给值,类似代码中的4。当然,在定义函数时,想要给形参给默认值,也不要跨越给值,这在C++中是不允许的。
注意:在定义函数时,给形参给值,只是给了一个默认值,在后面的主函数中调用时,这个值是可以改变的,比如fun(10, 20, 30);
上述代码的运行结果:
在这里插入图片描述

1.2 缺省参数在多文件中(.h,.cpp)

首先创建一个头文件,C.h,写入函数fun的声明,在.cpp中定义后进行运行,即:

//C.h文件
void fun(int a, int b = 20, int c = 100);
//void fun(int, int = 20,int = 100);
//.cpp文件
void fun(int a, int b = 0, int c = 10)
{
	cout << " a = " << a << " b = " << b << " c = " << c << endl;
}

int  main()
{
	fun(10);
	fun(10, 20);
	fun(10, 20, 30);

	return 0;
}

这种情况在C++中是不允许的,因为在.cpp中,加的头文件是#include<C.h>,说明我们在.h中已经给了b和c的值,如果在.cpp中再给,那么在主函数中调用时,应该用那个形参的默认值,这就产生了矛盾,因此可以选择在其中一个中指定缺省参数值,即定义或者声明中二选一,那么正确的代码为:

//C.h文件
//函数声明
void fun(int a, int b = 20, int c = 100);
//void fun(int, int = 20,int = 100);

//.cpp文件
void fun(int a, int b , int c )
{
	cout << " a = " << a << " b = " << b << " c = " << c << endl;
}

int  main()
{
	fun(10);
	fun(10, 20);
	fun(10, 20, 30);

	return 0;
}

运行结果:
在这里插入图片描述
注意:习惯上,缺省参数在公共头文件包含的函数声明中指定,不要在函数的定义中指定;如果在函数的定义中指定缺省参数值,在公共头文件包含的函数声明中不能再次指定缺省参数值。

1.3 缺省实参可以使用任意表达式

例:先定义一个产生随机值的函数,有

int my_rand()
{
	int ra = rand() % 100;
	return ra;
}

再定义一个函数,去调用上面产生随机值的函数

void fun(int a, int b = my_rand())
{
	cout << " a = " << a << " b = " << b << endl;
}

主函数中进行调用

int main()
{
	fun(12);
	fun(12, 13);
	return 0;
}

运行结果:
在这里插入图片描述
总结:说明缺省实参不一定是我们常见的常量表达式,也可以使用任意表达式;而且,在主函数中进行调用时也是可以两个值都给,也可以只给出a的值,这些调用程序执行都是OK的。
注意:当缺省实参是一个表达式时,在函数调用时该表达式被求值。
C语言是不支持缺省参数这种形式。

2. 函数重载

2.1 C中函数调用实现

int max_i(int a, int b)
{
	return a > b ? a : b;
}
double max_d(double a, double b)
{
	return a > b ? a : b;
}
char max_c(char a, char b)
{
	return a > b ? a : b;
}
int main()
{
	int s1 = max_i(10, 20);
	double s2 = max_d(10.11, 20.22);
	char s3 = max_c('a', 'b');
	printf("s1 = %d,s2 = %lf,s3 = %c\n", s1, s2, s3);
	return 0;
}

运行结果:
在这里插入图片描述
分析:这三个函数都执行了相同的操作,都是返回两个形参中的最大值,从用户的角度来看,只有一种操作,就是判断最大值,至于具体怎么完成的细节,用户一点也不关心;其实,这种词汇上的复杂性不是“判断参数中的最大值”问题本身固有的,而是反映了C中的一种局限性:在同一个域中出现的函数名字必须指向一个函数体,也就是在同一个.c文件中不能出现相同的函数名字,从侧面来说,C中定义函数时,函数名是不能重复的,并且函数调用时只看函数名来调用
注意:那么我们想想,程序很复杂,很多时,会出现非常多的函数名,在主函数中调用时,程序员必须一个一个的去找函数名,这会很麻烦,因此,为了解决这一问题,在C++中出现了“函数重载”这一概念。

1.2 函数重载

1.2.1 函数重载定义

在C++中可以为两个或者两个以上的函数提供相同的函数名称,只要参数类型不同,或参数类型相同而参数个数不同,称为函数的重载。
通过如下的代码更深一步了解函数重载的定义:

int my_max(int a, int b)
{
	return a > b ? a : b;
}
double my_max(double a, double b)
{
	return a > b ? a : b;
}
char my_max(char a, char b)
{
	return a > b ? a : b;
}
int main()
{
	int s1 = my_max(10, 20);
	double s2 = my_max(10.11, 20.22);
	char s3 = my_max('a', 'b');
	cout << " s1 = " << s1 << " s2 = " << s2 << " s3 = " << s3 << endl;
	return 0;
}

运行结果:
在这里插入图片描述
总结:上述程序中每个同名函数的参数是唯一的,也就是,当一个函数名在同一个域中被声明多次时,编译器会按照如下的步骤解释第二个的声明,如果两个函数的参数表中的个数或类型或顺序不同,则认为这两个函数是重载,
在这里插入图片描述

1.2.2 判断函数重载的规则

(1)如果两个函数的参数表相同,但是返回类型不同,会被标记为编译错误:函数的重复申明
在这里插入图片描述
(2)参数表的比较过程与形参名无关
在这里插入图片描述
运行后会报错:
在这里插入图片描述
总结:其实上面两个函数就是定义的同一个函数,只是将变量名字换了。
(3)如果在两个函数的参数表中,只有缺省参数不同,则第二个声明被视为第一个的重复声明,如
在这里插入图片描述
这说明,上面两个print函数是一样的。
(4)typedef名为现有的数据类型提供了一个替换名,它并没有创建一个新类型,因此,如果两个函数参数表的区别只在于一个使用了typedef,而另一个使用了与typedef相应的类型,则该参数表被视为相同的参数列表,如:
在这里插入图片描述
(5)当一个形参类型有const或volatile修饰时,如果形参是按值传递方式定义,在识别函数声明是否相同时,并不考虑const和volatile修饰符,如:
在这里插入图片描述
(6)当一个形参类型有const或volatile修饰时,如果形参定义指针或引用时,在识别函数声明是否相同时,就要考虑const和volatile修饰符,如:
在这里插入图片描述
(7)注意函数调用的二义性:
如果两个函数的参数表中,形参类型相同,而形参个数不同,形参默认值将会影响函数的重载,如:
在这里插入图片描述
在函数调用时,fun(10),应该调用函数1和3哪个?fun(10,20)应该调用2和3哪个函数?这就有了二义性。
(8)函数重载解析的步骤
①确定函数调用考虑的重载函数的集合,确定函数调用中实参表的属性;
②从重载函数集合中选择函数,该函数可以在(给出实参个数和类型)的情况下可以调用函数;
③选择与调用最匹配的函数。

1.2.3 名字粉碎(名字修饰)

C或者C++函数在内部(编译和链接)通过修饰名识别,修饰名是编译器在编译函数定义或者原型时生成的字符串;修饰名由函数名、类名、调用约定、返回类型、参数等共同决定。
(1)调用约定:
_stdcall是Pascal程序的缺省调用方式,通常用于win 32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。
C调用约定(即用_cdecl关键字说明)按从右至左的顺序压参数入栈,由调用者把参数弹出栈,对于传递参数的内存栈是由调用者来维护的(正因为如此,实现可变参数的函数只能使用该调用约定)。
_fastcall调用约定是“人”如其名,它的主要特点是快,因为它是通过寄存器来传递参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它与前两者均不同。
thiscall仅仅应用于“C++”类的成员函数,this指针存放于ECX寄存器,参数从右到左压,thiscall不是关键词,因此不能被程序指定。
在C/C++中,一个程序需要运行起来,需要经历以下几个阶段:预编译(预处理)、编译、汇编、链接。
(2)C语言编译时函数名修饰约定规则:
C语言的名字修饰规则非常简单,_cdecl是C/C++的缺省调用方式,调用约定函数名字前面添加了下划线前缀。
例:格式:_functionname
在这里插入图片描述
例:@functionname@number;
_fastcall调用约定在输出函数名前加上一个"@"符号,函数名后面也是一个“@”符号和其他参数的字节数。

在这里插入图片描述
(3)C++编译时函数名修饰约定规则:
_cdecl调用约定:
①以“?”标识函数名的开始,后跟函数名;
②函数名后面以“@@YA”标识参数表的开始,后跟参数表;
③参数表以代号表示;
X——void
D——char
E——unsigned char
F——short
H——int
I——unsigned int
J——long
K——unsigned long
M——float
N——double
_N——bool
PA——表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以“0”代替,一个“0”代表一次重复。
参数表的第一项为该函数的返回类型,其后依次为参数的数据类型,指针标识在其所指数据类型前。
参数表后以“@Z”标识整个名字的结束,如果该函数无参数,则以“Z”标识结束。
例:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
C++函数是重载
例如我们刚开始写的函数:
在这里插入图片描述
关键字
extern “C”:函数名以C的方式修饰约定规则;
extern “C++”:函数名以C++的方式修饰约定规则;
总结:在C++中函数可以重载,是因为名字粉碎操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值