昨天面试的时候,被问到函数重载时第三个参数为空,只有两个参数会不会调用三个参数的函数,我脱口而出就说不会,回来想想感觉好像不对,然后查了一下资料,发现真的错了
例子:int fun(int a,int b,int c=0)
fun(5,8)是可以调用上面那个函数的,但如果又存在一个函数int fun(int a,int b)
则,fun(5,8)因不知道调用哪个会出错。
详解如下:
一.形参&实参
形参和实参,虽然用了这么久了,不过概念上还是有点纠结的。这里简单总结一下:形参是说明参数类型的,实参就是函数实际操作的对象,我们定义一个函数的时候,写的那个是形参,我们调用函数的时候,给如的参数就是实参。
最近在百度知道上看到了一个关于形参实参最精辟的解释,无耻的引用一下:
比如说进女厕所,那就是女人才能进去 ,那么女人就是进女厕所这个操作的形参,林黛玉进去了,杨贵妃进去了,林黛玉,杨贵妃这些就是实参,李隆基要进的话那就类型不符
二.简单使用
C++函数支持默认参数,这是一个很方便的特性。我们在函数声明或者定义的时候,给函数的参数设置一个默认值,当调用时如果不给参数或者给出一部分参数,那么就使用函数设定的默认参数值。先看一个例子:
- // C++Test.cpp : 定义控制台应用程序的入口点。
- //
- #include "stdafx.h"
- #include <iostream>
- using namespace std;
- void DefaultArguTest(int arg1, int arg2 = 2, int arg3 = 3)
- {
- cout<<arg1<<" "<<arg2<<" "<<arg3<<endl;
- }
- int _tmain(int argc, _TCHAR* argv[])
- {
- //第2,3个参数给出了,则使用参数的值
- cout<<"No Default argu:"<<endl;
- DefaultArguTest(1,1,1);
- //第3个参数没给出,则使用默认值
- cout<<"Default argu3:"<<endl;
- DefaultArguTest(1,1);
- //第2,3个参数都没给出,使用默认值
- cout<<"Default argu2,3:"<<endl;
- DefaultArguTest(1);
- system("pause");
- return 0;
- }
No Default argu:
1 1 1
Default argu3:
1 1 3
Default argu2,3:
1 2 3
请按任意键继续. . .
三.注意事项
四.默认参数和函数重载的冲突
- // C++Test.cpp : 定义控制台应用程序的入口点。
- //
- #include "stdafx.h"
- #include <iostream>
- using namespace std;
- //函数声明
- void DefaultArguTest(int arg1 = 1, int arg2 = 2, int arg3 = 3);
- //重载
- void DefaultArguTest();
- int _tmain(int argc, _TCHAR* argv[])
- {
- //不给参数
- DefaultArguTest();
- system("pause");
- return 0;
- }
- //函数定义
- void DefaultArguTest(int arg1, int arg2, int arg3)
- {
- cout<<arg1<<" "<<arg2<<" "<<arg3<<endl;
- }
错误如下:
error C2668: “DefaultArguTest”: 对重载函数的调用不明确
1> 可能是“void DefaultArguTest(void)”
1> 或 “void DefaultArguTest(int,int,int)”
对于当我们不给参数的时候,默认的DefaultArguTest和无参数的DefaultArguTest都可能被调用,所以就造成了调用不明确的错误。
仔细想一下,为什么C++的默认构造函数在我们自己定义了构造函数就自动不生成了呢?
个人感觉,有可能是害怕我们自己定义构造函数时,如果加上默认参数,那么就和编译器为我们提供的默认构造函数冲突了,为了防止这种隐患,索性如果自己写了构造函数,那就不生成默认构造函数了。
五.覆写函数时不要更换默认参数
- // C++Test.cpp : 定义控制台应用程序的入口点。
- //
- #include "stdafx.h"
- #include <iostream>
- #include <string>
- using namespace std;
- class Base
- {
- public:
- virtual void Print(int i = 1, int j = 2)
- {
- cout<<"In base: "<<i<<" "<<j<<endl;
- }
- };
- class Child : public Base
- {
- //我们覆写带有默认参数的函数,VA插件给出了提醒,这两个值都是有默认参数的
- void Print(int i /* = 1 */, int j /* = 2 */)
- {
- cout<<"In Child: "<<i<<" "<<j<<endl;
- }
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- Base* base = new Child();
- base->Print();
- system("pause");
- return 0;
- }
请按任意键继续. . .
但是,如果我们不信邪,偏偏要给子类加一个不同的默认参数,结果就会大大出乎我们的意料:
- // C++Test.cpp : 定义控制台应用程序的入口点。
- //
- #include "stdafx.h"
- #include <iostream>
- #include <string>
- using namespace std;
- class Base
- {
- public:
- virtual void Print(int i = 1, int j = 2)
- {
- cout<<"In base: "<<i<<" "<<j<<endl;
- }
- };
- class Child : public Base
- {
- public:
- //我们手动的将默认参数修改了
- void Print(int i = 3, int j = 4 )
- {
- cout<<"In Child: "<<i<<" "<<j<<endl;
- }
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- //静态绑定
- cout<<"Static bind:"<<endl;
- Child* child = new Child();
- child->Print();
- //动态绑定
- cout<<"Dynamic bind:"<<endl;
- Base* base = new Child();
- base->Print();
- system("pause");
- return 0;
- }
Static bind:
In Child: 3 4
Dynamic bind:
In Child: 1 2
请按任意键继续. . .
第一个没有问题,子类指针调用子类函数,输出的结果也是子类给出的默认参数。但是,第二个问题就大了,我们明明触发了多态,但是,输出的结果竟然是基类给出的那两个默认参数的值!!!
为什么会这样?因为为了效率,函数的默认参数是使用静态绑定的,换句话说,不管你有没有多态,我只关心你用什么指针来调,基类指针就调用基类的默认参数,子类指针就给出子类的默认参数。而不像我们多态那样,会发生动态绑定,可以用基类指针调用子类函数。而我们在一个动态绑定的函数中使用了静态绑定的参数,结果肯定是不对的!
所以,正如《Effective C++》中所说:“绝不重新定义继承而来的缺省参数”!