考虑以下计划:
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
}
如果我从const
类Base
方法中删除foo
,则会调用Derived::foo()
。
我似乎无法理解这种行为。
1)这种行为的原因是什么?
2)这是在编译时还是在运行时决定的?
由于
答案 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() const
与void foo()
的功能不同。你根本不是压倒一切。这就是原因。