37

任何人都可以解释 C++ 编译器在哪里保留虚函数参数的默认值吗?我知道在子类中更改这些参数是个坏主意,但为什么呢?谢谢。

4

2 回答 2

51

这是一个坏主意,因为它们没有保存在任何地方。

使用的默认值将是那些在静态(编译时)类型中定义的值。因此,如果您要更改覆盖中的默认参数,但您通过基类指针或引用调用函数,则将使用基类中的默认值。

#include <iostream>

struct Base
{
    virtual ~Base(){ }
    virtual void foo(int a=0) { std::cout << "base: " << a << std::endl; }
};

struct Derived : public Base
{
    virtual ~Derived() { }
    virtual void foo(int a=1) { std::cout << "derived: " << a << std::endl; }
};

int main()
{
    Base* derived = new Derived();
    derived->foo();    // prints "derived: 0"
    delete derived;
}
于 2013-08-07T14:07:46.367 回答
5

为虚函数提供默认参数初始化器往往会破坏多态性并将不必要的复杂性引入类层次结构。

考虑以下不合规代码

class Thing {
  public:
    virtual ~Thing();
    virtual void doItNTimes( int numTimes = 10 );
    virtual void doItThisHigh( double howHigh = 1.0 );
    // ...
};
class MyThing : public Thing {
  public:
    void doItNTimes( int numTimes );
    void doItThisHigh( double howHigh = 2.2 );
    // ...
};

默认初始化器不是函数签名类型的一部分,也不参与覆盖或隐藏。因此,此不合规代码示例中显示的两个基类虚函数都在派生类中被覆盖。但是,基类和派生类接口中默认参数初始值设定项状态的差异会导致行为差异,具体取决于用于访问对象的接口。

MyThing *mt = new MyThing;
Thing *t = mt;
t->doItNTimes(); // default of 10
mt->doItNTimes();  // compile time error!
t->doItThisHigh(); // default of 1.0!
mt->doItThisHigh(); // default of 2.2

在这段代码中,MyThing 类的设计者的意图并不清楚。据推测,对于 MyThing 类型的对象来说,doItThisHigh 的默认值是 2.2 很重要。但是,当通过其 Thing 接口操作 MyThing 时,是否也应默认使用该值尚不清楚。

更多详情请参考以下链接https://www.securecoding.cert.org/confluence/display/cplusplus/OOP04-CPP.+Prefer+not+to+give+virtual+functions+default+argument+initializers

于 2013-08-07T14:43:50.033 回答