C++-函数的默认参数问题?

发布于 2017-01-07 22:03:09 字数 519 浏览 1205 评论 4

先看下面的代码

#include <iostream>
using namespace std;

class Base
{
public:
virtual void fun(int b = 1)
{
cout << "Base: " << b << endl;
}
};

class Derived : public Base
{
public:
virtual void fun(int b = 2)
{
cout << "Derived: " << b << endl;
}
};

int main()
{
Base* B1 = new Base;
Base* B2 = new Derived;
B1->fun();
B2->fun();
return 0;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

瑾兮 2017-06-04 23:03:02

这是因为你的:

 Base* B1 = new Base;
Base* B2 = new Derived;

中B2指针是Base类型(基类型),所以B2->调用的是从基类继承下来的fun()函数,所以,输出的结果也是1.如果定义Derived*B3的话,肯定会输出2.

虐人心 2017-04-02 02:37:49

C++虚函数的默认参数问题,刚在Effective C++ 中的38条有说明
让我们从一开始就把问题简化。缺省参数只能作为函数的一部分而存在;另外,只有两种函数可以继承:虚函数和非虚函数。因此,重定义缺省参数值的唯一方法是重定义一个继承而来的函数。然而,重定义继承而来的非虚函数是一种错误(参见条款37),所以,我们完全可以把讨论的范围缩小为 "继承一个有缺省参数值的虚函数" 的情况。
既然如此,本条款的理由就变得非常明显:虚函数是动态绑定而缺省参数值是静态绑定的。
什么意思?你可能会说你不懂这些最新的面向对象术语;或者,过度劳累的你一时想不起静态和动态绑定的区别。那么,让我们来复习一下。
对象的静态类型是指你声明的存在于程序代码文本中的类型。看下面这个类层次结构:

  ...

};

class Rectangle: public Shape {

public:

// 注意:定义了不同的缺省参数值 ---- 不好!

virtual void draw(ShapeColor color = GREEN) const;

...

};

class Circle: public Shape {

public:

virtual void draw(ShapeColor color) const;

...

};

现在看看这些指针:

 Shape *ps; // 静态类型 = Shape*
Shape *pc = new Circle; // 静态类型 = Shape*
Shape *pr = new Rectangle; // 静态类型 = Shape*

这个例子中, ps, pc,和pr都被声明为Shape指针类型,所以它们都以此作为自己的静态类型。注意,这和它们真的所指向的对象的类型绝对没有关系 ---- 它们的静态类型总Shape*。

对象的动态类型是由它当前所指的对象的类型决定的。即,对象的动态类型表示它将执行何种行为。上面的例子中,
pc的动态类型是Circle
pr的动态类型是Rectangle

至于ps,实际上没有动态类型,因为它(还)没有指向任何对象。
动态类型,顾名思义,可以在程序运行时改变,典型的方法是通过赋值:

 ps = pc; // ps的动态类型
// 现在是Circle*
ps = pr; // ps的动态类型
// 现在是Rectangle*

虚函数是动态绑定的,意思是说,虚函数通过哪个对象被调用,具体被调用的函数就由那个对象的动态类型决定:

 pc->draw(RED); // 调用Circle::draw(RED)
pr->draw(RED); // 调用Rectangle::draw(RED)

我知道这些都是老掉牙的知识了,你当然也了解虚函数。(如果想知道它们是怎么实现的,参见条款M24)但是,将虚函数和缺省参数值结合起来分析就会产生问题,因为,如上所述,虚函数是动态绑定的,但缺省参数是静态绑定的。这意味着你最终可能调用的是一个定义在派生类,但使用了基类中的缺省参数值的虚函数:
pr->draw(); // 调用Rectangle::draw(RED)!
这种情况下,pr的动态类型是Rectangle
所以Rectangle的虚函数被调用 ---- 正如我们所期望的那样。Rectangle::draw中,缺省参数值是GREEN。但是,由于pr的静态类型是Shape

这个函数调用的参数值是从Shape类中取得的,而不是Rectangle类!所以结果将十分奇怪并且出人意料,因为这个调用包含了Shape和Rectangle类中Draw的声明的组合。你当然不希望自己的软件以这种方式运行啦;至少,用户不希望这样,相信我。
不用说,ps, pc,和pr都是指针的事实和产生问题的原因无关。如果它们是引用,问题也会继续存在。问题仅仅出在,draw是一个虚函数,并且它的一个缺省参数在子类中被重新定义了。
为什么C++坚持这种有违常规的做法呢?答案和运行效率有关。如果缺省参数值被动态绑定,编译器就必须想办法为虚函数在运行时确定合适的缺省值,这将比现在采用的在编译阶段确定缺省值的机制更慢更复杂。做出这种选择是想求得速度上的提高和实现上的简便,所以大家现在才能感受得到程序运行的高效;当然,如果忽视了本条款的建议,就会带来混乱。

晚风撩人 2017-03-11 03:49:30

virtual 函数是动态绑定,而缺省参数值却是静态绑定的。
因为 B2 的静态类型是 Base* ,那么 B2 所指向的类的成员函数中的缺省参数值会静态绑定为 Base ,而 Base 中已经定义了 fun 的缺省参数值是 1,所以B2的调用也是输出 1。
C++以这种方式来运作的原因是考虑运行期效率,如果缺省参数值是动态绑定的,编译器就必须有某种办法在运行起为 virtual 函数决定适当的参数缺省值,这比目前实行的“在编译器决定”的机制更慢而且更复杂。为了程序的执行速度和编译器实现上的简易度,C++ 做了这样的取舍,其结果就是如今享受的执行效率。

归属感 2017-01-08 03:18:41

对于这个问题,先通过VS2008反编译一下,main函数的反编译的部分代码如下图所示:

从上图可以看出,B1->fun();编译的代码中第二行是push 1,而B2->fun();编译的代码中第二行也是push 1!即调用它们时都是push 1来压栈的,所以都会输出1!
那么现在可以来解释这个问题了。

首先需要知道的是,虚函数的动态绑定的,但是缺省参数却是静态绑定的
下面说明为什么缺省参数是静态绑定的。
在编译器编译程序的过程中,当调用fun()函数的时候,由于fun()函数是需要参数的,而又没有传递进去参数,那么这个时候编译器就会把参数的默认值加上去。此时如果编译器发现参数没有默认值就会报错了。而由于这个时候还处于编译期,编译器根本无法知道B1和B2的实际类型,只知道B1和B2的静态类型,也就是Base类型,那么编译器当然就是只会查看Base类中fun()的参数的默认值,并把该默认值拿过来用了。而如果编译器发现Base类中fun()的参数没有默认值的话,就会报错的。实验如下,把原代码中Base类的fun()的参数去掉默认值,再次编译,就会报错,错误信息如下图所示:

没错,编译器只会检查基类Base类中的fun()函数的参数是否有默认值,而不会去找派生类。为什么会这样呢?因为在编译期,编译器认定的B1和B2就是基类Base类的对象,对fun()函数的调用就是基类Base类的fun()函数,所以会只查找基类的信息。
从这里也可以知道一点,就是派生类在虚函数中给参数设置默认值是没有用的,根本不会被用到。因为参数的默认值是在编译期就发生作用的,而派生类只有在运行期才会起作用。

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文