2011-09-21 196 views
8

任何人都可以解釋下面代碼的輸出嗎?虛擬函數在基類中是常量而在派生中不是常量

#include <iostream> 
#include <string> 
class Animal 
{ 
public: 
    Animal(const std::string & name) : _name(name) { } 
    ~Animal() { } 
    virtual void printMessage() const 
    { 
     std::cout << "Hello, I'm " << _name << std::endl; 
    } 

private: 
    std::string _name; 
    // other operators and stuff 
}; 

class Cow : public Animal 
{ 
public: 
    Cow(const std::string & name) : Animal(name) { } 
    ~Cow() { } 
    virtual void printMessage() 
    { 
     Animal::printMessage(); 
     std::cout << "and moo " << std::endl; 
    } 
}; 

int main() { 
    Cow cow("bill"); 
    Animal * animal = &cow; 
    cow.printMessage(); 
    animal->printMessage(); 
} 

輸出是

你好,我是比爾
和哞
你好,我是比爾

我不明白爲什麼。指針動物指向Cow類型的對象。 printMessage是一個虛函數。爲什麼牛類的實現不是被調用的呢?

+0

+1爲完整的最小工作示例 – Flexo

+0

您是否嘗試將Cow類中的函數更改爲virtual void printMessage()const? –

回答

12

Cow不會覆蓋虛擬函數Animal,因爲它具有不同的簽名。實際發生的是Cow隱藏函數Animal

這樣做的結果是,在一個Animal調用printMessage將只使用版本Animal,無論一個在Cow(它不覆蓋它),但是從Cow調用它會使用一個在Cow(因爲它隱藏了一個從Animal)。

要解決此問題,請刪除Animal中的const,或在Cow中添加const

在C++ 2011,您將能夠使用override關鍵字來避免這樣的微妙陷阱:

class Cow : public Animal 
{ 
public: 
    Cow(const std::string & name) : Animal(name) { } 
    ~Cow() { } 
    virtual void printMessage() override 
    { 
     Animal::printMessage(); 
     std::cout << "and moo " << std::endl; 
    } 
}; 

通知printMessage()後加入override。如果printMessage沒有實際覆蓋基類版本,這將導致編譯器發出錯誤。在這種情況下,你會得到錯誤。

+2

如果基類函數必須是const並且派生的函數必須是非const的,那麼可以使用非虛函數接口語法,其中基函數不是虛擬的,但是調用一個非常量的私有虛擬接口語言,因此可以被派生類中的非const函數覆蓋。 – derpface

+0

謝謝derpface,正是我需要:) –

6

您有兩個不同版本的printMessage,其中一個是const,另一個不是。儘管它們有相同的名字,但它們是無關的。 Cow中的新函數隱藏了Animal中的函數,因此,當您直接調用編譯器時,編譯器僅考慮Cow版本。

0

剛剛嘗試過。將const添加到牛類,它將工作。

virtual void printMessage() const這兩個類。

1

我花了一段時間來了解彼得·亞歷山大的隱藏答案,但另一種方式來了解其計算方法如下:

說你拼寫錯誤的牛類中的方法名,但拼寫是否正確動物類:

Animal::printMessage() 
Cow::mispelledPrintMessage() 

然後當你有一個

Animal *animal; 

,你只能叫

animal->printMessage(); 

,但你不能叫

animal->mispelledPrintMessage(); 

因爲mispelledPrintMessage()不會在動物類存在。它的一個全新的方法在牛類中,所以它不能被多態調用通過一個基指針。

所以擁有Animal方法在簽名中有const,但在Cow方法中不是類似於派生類中稍微拼錯的方法名稱。 PS:另一個第四種解決方案(1使兩個方法都是const,2使兩個方法都是非常量,或者使用新的2011覆蓋關鍵字),就是使用一個強制類型來強制Animal指針變爲牛指針:

((Cow*)animal)->printMessage(); 

但這是一個非常醜陋的HACK,我不會推薦它。

PS:我總是試着寫我的toString()在基類中的簽名和所有派生類因爲這個原因與常量的方法。另外,如果在toString()簽名中包含const,則可以使用const const non-const對象調用toString()。假如你不是離開了常量,並試圖通過一個const對象調用toString(),GCC編譯器將與丟棄預選賽抱怨錯誤消息:

const Bad' as `this' argument of `std::string Bad::toString()' discards qualifiers 

此代碼:

#include <iostream> 
#include <string> 

class Bad 
{ 
public: 
     std::string toString() 
     { 
       return ""; 
     } 
}; 

int main() 
{ 
     const Bad  bad; 
     std::cout << bad.toString() << "\n"; 
     return 0; 
} 

因此,最後,由於Cow不會更改任何數據成員,所以最好的解決方案可能是在派生的Cow類中爲printMessage()添加const,以便BASE Animal和DERIVED Cow類在其簽名中具有const 。

丹尼斯比德納 -ahd 310

1

更正丹尼斯的職務。

請注意,您聲明您可以將動物(這是一隻動物*)施放到牛*的解決方案。但是,一旦Cow *您的動物類的printMessage將不可用。因此((Cow *)animal) - > printMessage()需要是((Cow *)animal) - > mispelledPrintMessage()

0

虛擬子類 - >不需要。您只需在子類中添加const以使其具有相同的簽名

相關問題