2011-06-10 121 views
5

考虑下面的程序:如果派生类重写方法,为什么调用基类方法?

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 
} 

如果我从Basefoo方法则Derived::foo()被称为删除const。 我似乎无法理解这种行为。

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

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

感谢

+0

1)不是重写2)是 – AJG85 2011-06-10 15:42:06

+0

如果签名不同,它不是重写! – 2011-06-11 01:18:45

回答

6

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

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?有没有任何规则,或只是碰巧是第一个选择?

它,因为静态objBase,和函数的名称是基于静态类型对象的解决。 Base甚至没有非常量版本。所以它没有被选中或拒绝的问题。它不存在于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; 
    } 
}; 

现在派生,两者的功能(常量和非const版本)可供选择,取消隐藏。现在几个有趣的问题。

由于现在Derived同时具有两个函数,所以函数将在下面的每个函数中调用?

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

+0

那么为什么const的版本是通过非const来选择的?有没有任何规则,或只是碰巧是第一个选择? – davka 2011-06-10 15:53:42

+0

@davka:查看编辑。 :-) – Nawaz 2011-06-10 16:04:59

+0

当然,愚蠢的我:(谢谢。有时你很习惯复杂性,你忘记了基本知识:) – davka 2011-06-10 16:09:29

3

你没有覆盖方法,因为Derived::foo是不完全一样的。

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

+1

请注意,它不必是_exactly_相同;覆盖的返回类型可能与被覆盖的函数的返回类型协变。 – 2011-06-10 15:42:32

+0

@James:参数类型也可能是逆变的。 – 2011-06-10 15:54:18

0

你在做什么叫做 “超载”。在重写中,正如@SLaks指出的那样,签名需要保持一致。

+1

它不是真的超载;它在隐藏。 – 2011-06-10 15:43:54

0

const是签名的一部分。 void foo() const是与void foo()不同的功能。你根本不在压倒一切。这就是为什么。

1

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

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

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

您可以分别重写这些函数。

就你而言,当你从非const对象调用foo时,你调用了该函数的非const变体。正如你重写了const变量,基类中的那个变量是被调用的变量。

相关问题