2015-06-10 65 views
5

我不明白這段代碼有什麼問題。它看起來像一個驚人的陷阱!調用虛擬函數時出現奇怪的行爲

此代碼:

class Foo 
{ 
    public: 
     virtual double foo(double x) const = 0; 
       double foo(int x) const { return (double)(x + x); } 
}; 

class Bar : public Foo 
{ 
    public: 
     virtual double foo(double x) const { return x * x; } 
}; 

int main() 
{ 
    Bar* b = new Bar; 
    Foo* f = b; 
    std::cout << b->foo(3) << " " << f->foo(3) << std::endl; 
    std::cout << b->foo(5.0) << " " << f->foo(5.0) << std::endl; 
    return 0; 
} 

打印以下輸出:

9 6 
25 25 

我推斷Bar::foo(double) const調用與隱式轉換時的指針的類型是Bar*。但是爲什麼這樣的事情可能沒有任何警告?

我使用GCC 4.7.2。我使用g++ -Wall foobar.cpp -o foobar.exe

+2

我認爲這將是更好,如果你選擇你的第二個號的另一個大於2由於2×2 = 4, 2 + 2 = 4。 – nabroyan

+1

我很好奇,如果'foo(int)'的返回值也是'int',你有同樣的問題嗎?確實,使用相同的值(但不同的類型)會更好:'3'和'3.0'。 – Kryptos

+1

http://stackoverflow.com/questions/1628768/why-does-an-overridden-function-in-the-derived-class-hide-other-overloads-of-the和http:// stackoverflow可能出現重複。 com/questions/411103/function-with-same-name-but-different-signature-in-derived-class – Kryptos

回答

9

這是由於名字隱藏。

當您在Bar中聲明一個名爲foo的函數時,可以在Foo中隱藏所有具有相同名稱的聲明。

這樣,當靜態類型的指針是Bar,編譯器發現在Bar版本,這需要double,所以它隱含轉換int滿足這一點。

如果你想int版本Foo可見,加using聲明:

class Bar : public Foo 
{ 
    public: 
     using Foo::foo; 
//  ^^ makes the versions in Foo visible 
     virtual double foo(double x) const { return x * x; } 
}; 
2

編譯當類型爲Bar*時,只有一個版本的方法可見,即使用double參數。

隱藏具有相同名稱(但簽名不同)的基礎方法。

要使它們可用,您可以在派生類中使用using Foo::foo

根據你的編譯器,我認爲你也可能會得到關於隱式轉換的警告,或者你顯然想調用一個隱藏的方法。

1

Foo有兩個過載foo,一個需要double和另一個需要int

Bar有一個foo過載,需要double。這個重載隱藏了基類中所有具有相同名字的函數。這被稱爲名稱隱藏

甲解決將是採用使用聲明使從基類的其他foo重載在Foo派生類的範圍:

class Bar : public Foo 
{ 
    public: 
     using Foo::foo; 
     virtual double foo(double x) const { return x * x; } 
}; 
相關問題