虚函数C++中的默认参数
default parameter in virtual functions C++
我了解了 C++ 中的继承机制和虚函数。
根据我的知识(在我遇到的所有示例中),继承的方法与父方法具有相同的签名 class'。
我的问题如下:
我知道函数默认参数值不是函数签名的一部分。
我能否将此值定义为父 Class 虚函数中的某个常量,并在派生 class 中声明并实现没有此默认值的覆盖方法。
在这种情况下,当我使用指向父对象的指针调用派生对象的方法时 class,该函数是否会被调用 with/without 这个默认初始化?
感谢
默认参数主要是语法糖,在编译时确定。另一方面,虚拟调度是 运行 时间的功能。选择与实际调用的函数一起定义的默认参数可能是最不令人惊讶的,但由于上述原因,这是不可能的(至少在没有额外的 运行 时间开销的情况下)。
因此,默认参数由编译器使用调用成员函数的对象的 static 类型选择。让我们看一个例子。
#include <iostream>
#include <memory>
class Base
{
public:
virtual void
f(int a, int b = 1)
{
std::cout << "Base: a = " << a << ", b = " << b << "\n";
}
};
class Derived : public Base
{
public:
virtual void
f(int a = 1, int b = 2) override
{
std::cout << "Derived: a = " << a << ", b = " << b << "\n";
}
};
int
main()
{
std::unique_ptr<Base> base_as_base {new Base {}};
std::unique_ptr<Base> derived_as_base {new Derived {}};
std::unique_ptr<Derived> derived_as_derived {new Derived {}};
base_as_base->f(0); // Base: a = 0, b = 1
derived_as_base->f(0); // Derived: a = 0, b = 1
// derived_as_base->f(); // compiler error
derived_as_derived->f(0); // Derived: a = 0, b = 2
derived_as_derived->f(); // Derived: a = 1, b = 2
}
我同意这令人困惑。请不要写这样的代码。幸运的是,有一个简单的解决方法。除了根本不使用默认参数外,我们还可以使用一个叫做 non-virtual interfaces 的惯用语。虚函数是 protected
并且没有给出任何默认参数。然后它只能由基 class 中的非 virtual
函数间接调用。该函数可以在一个地方定义所有默认参数。
#include <iostream>
#include <memory>
class Base
{
public:
void
f(int a, int b = 1)
{
this->impl(a, b);
}
protected:
virtual void
impl(int a, int b)
{
std::cout << "Base: a = " << a << ", b = " << b << "\n";
}
};
class Derived : public Base
{
protected:
virtual void
impl(int a, int b) override
{
std::cout << "Derived: a = " << a << ", b = " << b << "\n";
}
};
int
main()
{
std::unique_ptr<Base> base_as_base {new Base {}};
std::unique_ptr<Base> derived_as_base {new Derived {}};
std::unique_ptr<Derived> derived_as_derived {new Derived {}};
base_as_base->f(0); // Base: a = 0, b = 1
derived_as_base->f(0); // Derived: a = 0, b = 1
derived_as_derived->f(0); // Derived: a = 0, b = 1
}
这是我从 C++ 草案标准 N3337 中找到的内容:
8.3.6 Default arguments
10 A virtual function call (10.3) uses the default arguments in the declaration of the virtual function determined by the static type of the pointer or reference denoting the object. An overriding function in a derived class does not acquire default arguments from the function it overrides. [ Example:
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()
}
—示例结束]
回答你的问题:
Can I define this value to be some constant in the parent Class virtual function,
是
and in the derived class declare and implement the overriding method without this default value
是的。
但是,当您使用派生的 class 指针调用该函数时,您必须提供一个参数。使用基本 class 指针调用函数时,无需提供参数。
我了解了 C++ 中的继承机制和虚函数。
根据我的知识(在我遇到的所有示例中),继承的方法与父方法具有相同的签名 class'。
我的问题如下: 我知道函数默认参数值不是函数签名的一部分。
我能否将此值定义为父 Class 虚函数中的某个常量,并在派生 class 中声明并实现没有此默认值的覆盖方法。
在这种情况下,当我使用指向父对象的指针调用派生对象的方法时 class,该函数是否会被调用 with/without 这个默认初始化?
感谢
默认参数主要是语法糖,在编译时确定。另一方面,虚拟调度是 运行 时间的功能。选择与实际调用的函数一起定义的默认参数可能是最不令人惊讶的,但由于上述原因,这是不可能的(至少在没有额外的 运行 时间开销的情况下)。
因此,默认参数由编译器使用调用成员函数的对象的 static 类型选择。让我们看一个例子。
#include <iostream>
#include <memory>
class Base
{
public:
virtual void
f(int a, int b = 1)
{
std::cout << "Base: a = " << a << ", b = " << b << "\n";
}
};
class Derived : public Base
{
public:
virtual void
f(int a = 1, int b = 2) override
{
std::cout << "Derived: a = " << a << ", b = " << b << "\n";
}
};
int
main()
{
std::unique_ptr<Base> base_as_base {new Base {}};
std::unique_ptr<Base> derived_as_base {new Derived {}};
std::unique_ptr<Derived> derived_as_derived {new Derived {}};
base_as_base->f(0); // Base: a = 0, b = 1
derived_as_base->f(0); // Derived: a = 0, b = 1
// derived_as_base->f(); // compiler error
derived_as_derived->f(0); // Derived: a = 0, b = 2
derived_as_derived->f(); // Derived: a = 1, b = 2
}
我同意这令人困惑。请不要写这样的代码。幸运的是,有一个简单的解决方法。除了根本不使用默认参数外,我们还可以使用一个叫做 non-virtual interfaces 的惯用语。虚函数是 protected
并且没有给出任何默认参数。然后它只能由基 class 中的非 virtual
函数间接调用。该函数可以在一个地方定义所有默认参数。
#include <iostream>
#include <memory>
class Base
{
public:
void
f(int a, int b = 1)
{
this->impl(a, b);
}
protected:
virtual void
impl(int a, int b)
{
std::cout << "Base: a = " << a << ", b = " << b << "\n";
}
};
class Derived : public Base
{
protected:
virtual void
impl(int a, int b) override
{
std::cout << "Derived: a = " << a << ", b = " << b << "\n";
}
};
int
main()
{
std::unique_ptr<Base> base_as_base {new Base {}};
std::unique_ptr<Base> derived_as_base {new Derived {}};
std::unique_ptr<Derived> derived_as_derived {new Derived {}};
base_as_base->f(0); // Base: a = 0, b = 1
derived_as_base->f(0); // Derived: a = 0, b = 1
derived_as_derived->f(0); // Derived: a = 0, b = 1
}
这是我从 C++ 草案标准 N3337 中找到的内容:
8.3.6 Default arguments
10 A virtual function call (10.3) uses the default arguments in the declaration of the virtual function determined by the static type of the pointer or reference denoting the object. An overriding function in a derived class does not acquire default arguments from the function it overrides. [ Example:
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() }
—示例结束]
回答你的问题:
Can I define this value to be some constant in the parent Class virtual function,
是
and in the derived class declare and implement the overriding method without this default value
是的。
但是,当您使用派生的 class 指针调用该函数时,您必须提供一个参数。使用基本 class 指针调用函数时,无需提供参数。