虚函数 C++ 中的默认参数
Posted
技术标签:
【中文标题】虚函数 C++ 中的默认参数【英文标题】:default parameter in virtual functions C++ 【发布时间】:2015-01-15 23:06:19 【问题描述】:我读到了 C++ 中的继承机制和虚函数。
据我所知(在我遇到的所有示例中),继承的方法与父类具有相同的签名。
我的问题如下: 我知道函数默认参数值不是函数签名的一部分。
我能否在父类虚函数中将此值定义为某个常量,并在派生类中声明并实现没有此默认值的覆盖方法。
在这种情况下,当我使用指向父类的指针调用派生对象的方法时,会在有/没有这个默认初始化的情况下调用该函数吗?
谢谢
【问题讨论】:
嗯,你试过了吗?只需几分钟... @JanŽegklitz 我同意,如果您知道要尝试什么,您可以轻松地尝试一下,但它是一个令人困惑的情况,我认为这个答案可以解释正在发生的事情可能对许多人有用。毕竟,“我已经用我的编译器版本进行了尝试,并且发生了这种情况”并不是衡量 C++ 中实际工作情况的一个很好的衡量标准。 【参考方案1】:默认参数主要是语法糖,并在编译时确定。另一方面,虚拟调度是一种运行时特性。选择与实际调用的函数一起定义的默认参数可能是最不令人惊讶的,但由于上述原因,这是不可能的(至少在没有额外的运行时开销的情况下是不可能的)。
因此,编译器使用调用成员函数的对象的 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
我同意这令人困惑。请不要写这样的代码。幸运的是,有一个简单的解决方法。除了完全不使用默认参数外,我们还可以使用一种称为非虚拟接口的成语。虚函数是protected
并且没有给出任何默认参数。然后它只能由基类中的非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
【讨论】:
喜欢您关于使用带可选参数的非虚函数和不带可选参数的虚函数的建议。【参考方案2】:这是我从 C++ 标准草案 N3337 中找到的:
8.3.6 默认参数
10 虚函数调用 (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()
—结束示例 ]
回答你的问题:
我可以在父类虚函数中将此值定义为某个常量吗,
是的
并在派生类中声明并实现不带此默认值的覆盖方法
是的。
但是,当您使用派生类指针调用函数时,您必须提供一个参数。使用基类指针调用函数时,不需要提供参数。
【讨论】:
标准报价加 1。我一直在寻找官方参考资料,但没能很快找到。以上是关于虚函数 C++ 中的默认参数的主要内容,如果未能解决你的问题,请参考以下文章