代码执行派生类方法,但从基类方法获取默认参数

时间:2020-12-03 11:15:22

Can someone explain why the result of the code below would be "class B::1" ?

有人能解释一下为什么下面代码的结果是“B类:1”吗?

Why does the virtual method of derived class uses the default parameter of a base class and not his own? For me this is pretty strange. Thanks in advance!

为什么派生类的虚方法使用基类的默认参数,而不使用基类的默认参数?对我来说这很奇怪。提前谢谢!

Code:

代码:

#include <iostream>

using namespace std;

class A
{
public:
    virtual void func(int a = 1)
    {
        cout << "class A::" << a;
    }
};

class B : public A
{
public:
    virtual void func(int a = 2)
    {
        cout << "class B::" << a;
    }
};

int main()
{
    A * a = new B;
    a->func();

    return 0;
}

3 个解决方案

#1


6  

Because default arguments are resolved according to the static type of this (ie, the type of the variable itself, like A& in A& a;).

因为默认参数是根据这个变量的静态类型(例如,变量本身的类型,比如A& in a&a;)来解析的。

Modifying your example slightly:

略有修改你的例子:

#include <iostream>

class A
{
public:
    virtual void func(int a = 1)
    {
        std::cout << "class A::" << a << "\n";
    }
};

class B : public A
{
public:
    virtual void func(int a = 2)
    {
        std::cout << "class B::" << a << "\n";
    }
};

void func(A& a) { a.func(); }

int main()
{
    B b;
    func(b);
    b.func();

    return 0;
}

We observe the following output:

我们观察到以下输出:

class B::1
class B::2

In action at ideone.

在ideone行动。

It is not recommended that a virtual function change the default value for this reason. Unfortunately I don't know any compiler that warns on this construct.

由于这个原因,不建议虚拟函数更改默认值。不幸的是,我不知道任何编译器会对这个构造发出警告。


The technical explication is that there are two ways of dealing with default argument:

技术上的解释是有两种处理默认参数的方法:

  • create a new function to act as trampoline: void A::func() { func(1); }
  • 创建一个作为蹦床的新函数:void a:func() {func(1);}
  • add-in the missing argument at the call site a.func() => a.func(/*magic*/1)
  • add-in调用站点a.func()的缺失参数=> a.func(/*magic*/1)

If it were the former (and assuming that the A::func was declared virtual as well), then it would work like you expect. However the latter form was elected, either because issues with virtual were not foreseen at the time or because they were deemed inconsequential in face of the benefits (if any...).

如果是前者(并假设A::func也被声明为虚),那么它将像您期望的那样工作。但是,选择后一种形式,要么是因为当时没有预见到有关虚拟的问题,要么是因为在面对利益时,虚拟的问题被认为是无关紧要的(如果有的话…)。

#2


5  

Because default value is substituted during compilation and is taken from declaration, while real function to be called (A::func or B::func) is determined at runtime.

因为在编译过程中会替换默认值并从声明中获取,而在运行时确定要调用的真正函数(A:::func或B::func)。

#3


5  

Because polymorphism in C++ takes effect at run-time, whereas the substitution of default parameters takes effect at compile-time. At compile time, the compiler does not know (and is not supposed to know) the dynamic type of the object to which the pointer a points. Hence, it takes the default argument for the only type it knows for a, which in your example is A *.

因为c++中的多态性在运行时生效,而默认参数的替换在编译时生效。在编译时,编译器不知道(也不应该知道)指针指向的对象的动态类型。因此,它为它所知道的a的唯一类型取默认参数,在您的示例中是a *。

(This incidentally is also the reason default parameters are given in interfaces/headers rather than in implementations/definitions. The compiler never inserts the default parameter in the implementation's machine code, but only in the caller's machine code. Technically, the default parameter is the property of the caller; and the caller doesn't know -- and shouldn't need to know -- an object's dynamic type.)

(这也是为什么默认参数是在接口/头中给出的,而不是在实现/定义中给出的。编译器从不在实现的机器代码中插入默认参数,而是只在调用者的机器代码中。技术上,默认参数是调用方的属性;调用者不知道——也不应该知道——对象的动态类型。

#1


6  

Because default arguments are resolved according to the static type of this (ie, the type of the variable itself, like A& in A& a;).

因为默认参数是根据这个变量的静态类型(例如,变量本身的类型,比如A& in a&a;)来解析的。

Modifying your example slightly:

略有修改你的例子:

#include <iostream>

class A
{
public:
    virtual void func(int a = 1)
    {
        std::cout << "class A::" << a << "\n";
    }
};

class B : public A
{
public:
    virtual void func(int a = 2)
    {
        std::cout << "class B::" << a << "\n";
    }
};

void func(A& a) { a.func(); }

int main()
{
    B b;
    func(b);
    b.func();

    return 0;
}

We observe the following output:

我们观察到以下输出:

class B::1
class B::2

In action at ideone.

在ideone行动。

It is not recommended that a virtual function change the default value for this reason. Unfortunately I don't know any compiler that warns on this construct.

由于这个原因,不建议虚拟函数更改默认值。不幸的是,我不知道任何编译器会对这个构造发出警告。


The technical explication is that there are two ways of dealing with default argument:

技术上的解释是有两种处理默认参数的方法:

  • create a new function to act as trampoline: void A::func() { func(1); }
  • 创建一个作为蹦床的新函数:void a:func() {func(1);}
  • add-in the missing argument at the call site a.func() => a.func(/*magic*/1)
  • add-in调用站点a.func()的缺失参数=> a.func(/*magic*/1)

If it were the former (and assuming that the A::func was declared virtual as well), then it would work like you expect. However the latter form was elected, either because issues with virtual were not foreseen at the time or because they were deemed inconsequential in face of the benefits (if any...).

如果是前者(并假设A::func也被声明为虚),那么它将像您期望的那样工作。但是,选择后一种形式,要么是因为当时没有预见到有关虚拟的问题,要么是因为在面对利益时,虚拟的问题被认为是无关紧要的(如果有的话…)。

#2


5  

Because default value is substituted during compilation and is taken from declaration, while real function to be called (A::func or B::func) is determined at runtime.

因为在编译过程中会替换默认值并从声明中获取,而在运行时确定要调用的真正函数(A:::func或B::func)。

#3


5  

Because polymorphism in C++ takes effect at run-time, whereas the substitution of default parameters takes effect at compile-time. At compile time, the compiler does not know (and is not supposed to know) the dynamic type of the object to which the pointer a points. Hence, it takes the default argument for the only type it knows for a, which in your example is A *.

因为c++中的多态性在运行时生效,而默认参数的替换在编译时生效。在编译时,编译器不知道(也不应该知道)指针指向的对象的动态类型。因此,它为它所知道的a的唯一类型取默认参数,在您的示例中是a *。

(This incidentally is also the reason default parameters are given in interfaces/headers rather than in implementations/definitions. The compiler never inserts the default parameter in the implementation's machine code, but only in the caller's machine code. Technically, the default parameter is the property of the caller; and the caller doesn't know -- and shouldn't need to know -- an object's dynamic type.)

(这也是为什么默认参数是在接口/头中给出的,而不是在实现/定义中给出的。编译器从不在实现的机器代码中插入默认参数,而是只在调用者的机器代码中。技术上,默认参数是调用方的属性;调用者不知道——也不应该知道——对象的动态类型。