如果派生类重写方法,为什么要调用基类方法?

时间:2011-06-10 15:34:01

标签: c++ inheritance

考虑以下计划:

class Base {
public:
    virtual void foo() const {
        cout << "Base::foo()" << endl;
    }
};

class Derived : public Base {
public:
    virtual void foo() {
        cout << "Derived::foo()" << endl;
    }
};

void func(Base& obj) {
    obj.foo();
}

void main() {
    Derived d;
    func(d); // Base::foo() is printed 
}

如果我从constBase方法中删除foo,则会调用Derived::foo()。 我似乎无法理解这种行为。

1)这种行为的原因是什么?

2)这是在编译时还是在运行时决定的?

由于

5 个答案:

答案 0 :(得分:7)

在派生类中,函数签名是:

virtual void foo(); //Derived::foo

没有提及const。它是一个非const成员函数,而Base::foo const 成员函数。它们是两个不同的函数,因为const是函数签名的一部分。

virtual void foo() const; //Base::foo 

派生类不会覆盖此函数,而是添加另一个函数。

所以修复就是这样:

class Derived : public Base {
public:
    virtual void foo() const {
        cout << "Derived::foo()" << endl;
    }
};

由于const是函数签名的一部分。因此,当你打算覆盖base的foo时,你必须提及它。


@ davka问:

  

那么为什么选择const版本而不是const?是否有任何规则或恰好是第一个选择?

因为obj static 类型是Base,并且函数名称是根据对象的静态类型解析的。 Base甚至没有非const 版本。所以毫无疑问它被选中或被拒绝。它在Base中不存在。

void func(Base& obj) {
    obj.foo();  //calls Base::foo
}

但是,如果您将上述代码更改为以下代码:

void func(Derived & obj) {
    obj.foo();  //calls Derived:foo
}

现在将选择非const版本,因为Base::foo隐藏在Derived类中。


由于Derived::foo会隐藏Base::foo,因此您无法使用Derived的实例调用后者。

现在,让我们取消隐藏Base::foo并进行更多实验。

class Derived : public Base {
public:
    using Base::foo;         //<----------------this unhides Base::foo
    virtual void foo() {
        cout << "Derived::foo()" << endl;
    }
};

现在在Derived中,两个函数(const和非const版本)都可用,取消隐藏。现在几个有趣的问题。

由于Derived现在两个函数都是unhidden,下面的每个函数都会调用哪个函数?

void f(Derived& obj) {
    obj.foo(); //Which function? Base::foo or Derived::foo?
}
void g(const Derived & obj) {
    obj.foo(); //Which function? Base::foo or Derived::foo?
}

第一个将调用Derived::foo这是非const版本,第二个将调用Base::foo这是const版本。原因很简单,使用const对象,可以调用 const 函数,但是对于非const对象,可以调用两者,但是如果选择了非const版本,则它可用。

参见在线演示:http://www.ideone.com/955aY

答案 1 :(得分:3)

您没有覆盖该方法,因为Derived::foo并不完全相同。

要覆盖方法,基本和重写版本必须相同,包括const - ness。

答案 2 :(得分:1)

在C ++中,您可以使用两个具有相同名称和相同参数的函数,其中唯一的区别是const而另一个不是。

这个想法是你有时想要不同的行为。例如,访问函数函数可能具有不同的返回类型:

class MyClass
{
public:
  virtual Xxx * GetXxx();
  virtual Xxx const * GetXxx() const;
  // ....
}

您可以单独覆盖这些功能。

在您的情况下,当您从非const对象调用foo时,您调用了该函数的非const变体。当你重写const变量时,基类中的那个是被调用的那个。

答案 3 :(得分:0)

您正在做的事情称为“超载”。在重写中,正如@SLaks指出的那样,签名必须是相同的。

答案 4 :(得分:0)

const是签名的一部分。 void foo() constvoid foo()的功能不同。你根本不是压倒一切。这就是原因。