2011-06-10 92 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變量,基類中的那個變量是被調用的變量。

相關問題