C++多态性和默认参数

12

我有以下这些类:

class Base
{
    public:
        virtual void foo(int x = 0)
        {
            printf("X = %d", x);
        }
};

class Derived : public Base
{
    public:
        virtual void foo(int x = 1)
        {
            printf("X = %d", x);
        }
};

当我有:

Base* bar = new Derived();
bar->foo();

即使从Derived中调用foo,我的输出结果仍然是“X = 0”,但当我有以下代码时:

Derived* bar = new Derived();
bar->foo();
我的输出结果是"X = 1",这个行为是否正确?(从声明类型中选择默认参数值,而不是从实际对象类型中选择)。这会破坏C++的多态性吗?
如果有人在没有指定实际函数参数的情况下使用虚函数并使用函数的默认参数,这可能会引起很多问题。

1
在printf()中放置一些内容,实际上告诉你正在调用哪个函数。 - Ferruccio
很明显被调用的函数是Derived::foo。 - Mircea Ispas
如何区分它们?它们都会打印出类似“X = value”的内容。你不能使用这个值来确定哪一个被调用了。尝试在基类中打印“Base: X = %d”,你就能知道哪一个被调用了。 - Ferruccio
可能是虚函数可以有默认参数吗?的重复问题。 - Nawaz
3个回答

10

即使你覆盖了一个函数,其默认参数也会保留!而这种行为是正确的。让我从C++标准中查找参考资料。

C++标准中的§8.3.6/10 [Default arguments]指出:

虚函数调用(10.3)使用对象所表示的静态类型所确定的虚函数声明中的默认实参。派生类中重写的函数不会从其所重写的函数中继承默认实参

这里有一个来自标准本身的例子:

struct A {
     virtual void f(int a = 7);
};
struct B : public A {
     void f(int a);
};
void m()
{
    B* pb = new B;
    A* pa = pb;
    pa->f(); //OK, calls pa->B::f(7)
    pb->f(); //error: wrong number of arguments for B::f()
}

而且,不仅它被保留下来,每次调用函数时都会被评估:

§8.3.6/9说:

默认参数每次调用函数时都会被评估


@Nawaz:从另一个答案中复制/粘贴而不是链接并不真正帮助整个 SO 社区。特别是当其他人已经提供了链接时。 - Jon
@Jon:复制粘贴哪个答案?你是说我从另一个答案中复制粘贴的?哪个答案? - Nawaz
@Jon:好的,我现在说出你的答案。但如果你认为它回答了问题,那么你应该说这是重复的话题并投票关闭它! - Nawaz
1
@Nawaz:如果你仔细阅读问题,你会发现它们不是同一个问题。此外,你不觉得报复性地投反对票有点不成熟吗? - Jon
1
@Nawaz:我从来没有想过要隐瞒这个事实;我也解释了我的反对票。而且,难道不令人惊讶的是,我刚刚投的反对票消失了吗?啊,巧合……无论如何,祝你有美好的一天。 - Jon
显示剩余2条评论

5

@Jon,谢谢你提供的链接,原始答案肯定会得到我的点赞... - Nim
@Jon:如果您认为此链接回答了问题,那么您应该投票关闭此主题,因为它可能是重复的! - Nawaz
@Nawaz:正如我之前说过的:那个问题的答案也回答了这个问题。但是这些问题本身并不相同。 - Jon
@Jon:但你将链接作为回答提供给了这个问题。依照你的理解,除了阅读链接外,他还需要知道什么? - Nawaz
@Ferruccio:在我看来,这是个好主意,但这更像是个人偏好或团队惯例的问题。而且你会发现,说服那些受过“正统”C/C++教育的开发者将可读性放在简洁性之上是很困难的。 ;) - Jon
显示剩余2条评论

0

这就是C++的设计,我认为。

多态性是通过虚表完成的,它在内部使用函数指针进行操作,但参数的默认值并没有与函数指针一起存储,而是在编译阶段绑定,因此只能通过首先查看其类型来获取默认值,在您的第一个情况中,它是Base,然后将0用作默认值。


这在技术上是正确的,但作为答案却会误导人,因为C++标准并没有规定多态必须通过虚表来实现。它明确规定了在这种情况下应该发生什么;如果将其作为实现细节未定义,那么任何人都可以编写符合标准的编译器,使用类似虚表的机制来绑定默认值。 - Jon
是的。但对于那些从事标准工作的人来说,他们也会考虑这个功能是否实用。我只是认为它被设计成这样,因为另一种方式不太实用。任何函数都可以有许多参数,它们都可以有默认值。这使得运行时绑定变得不可能完成。 - Shuo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接