2012-09-18 166 views
1

結合超載和重寫的結果考慮下面的代碼C++中奇怪的行爲

class X{ 
public: 
    virtual void foo(X x){ } 
    virtual void foo(int index){ } 

}; 

class Y : public X{ 
public: 

    void foo(int index){ } 
}; 


int main(){ 
    Y y; 
    y.foo(X()); //Error, see below 
} 

X類已超載虛擬foo方法。一個版本需要X,另一個版本需要int。現在類Y繼承自X並覆蓋方法foo(int)。方法foo(X)不會被覆蓋,它應該保持不變。

然而,在main方法創建Y類型的對象和呼叫foo(X)時,編譯器會抱怨以下:

In function ‘int main()’: 
error: no matching function for call to ‘Y::foo(X)’ 
note: candidate is: 
note: virtual void Y::foo(int) 
note: no known conversion for argument 1 from ‘X’ to ‘int’ 

因此,唯一候選者是重寫foo(int)方法。看來另一種方法已經消失了。如果我刪除覆蓋版本,即聲明Ypublic Y : public X{};,那麼一切正常。爲什麼會發生?

回答

5

當一個派生類定義一個與相同名稱的成員作爲基類中的名稱時,派生類名隱藏了基類名。

在你的情況下,功能Y::foo隱藏X::foo。你需要把它納入的Y的範圍:

class Y : public X{ 
public: 

    using X::foo; //it brings X::foo into the scope of Y 

    void foo(int index){ } 
}; 
+0

這是爲什麼?這意味着,默認情況下,派生類違反了Liskov的原則,這對C++來說是一個非常糟糕的設計決定。 – gexicide

+3

@gexicide:實際上它並沒有違反LSP,因爲當你通過指針或對基類的引用訪問派生類*時,所有基類的方法仍然可以照常使用。 –

+0

@gexicide:這個語言決定的原因是不隱藏基類函數可能會導致意外的行爲。想象一下,代替'X',另一個基類超載需要一個'double'。類'Y'的用戶可能會調用'y.foo(2.5)',期望調用'Y :: foo(int)';相反,將調用'X :: foo(double)';用戶不希望存在的功能。 – Gorpik

1

重載適用於在同一範圍內定義的名稱。因此foo類別Y不會使類X中的foo過載,因爲它們是在不同的作用域中定義的。這被非正式地稱爲「名稱隱藏」。要將基類名稱拉入派生類,請添加使用指令:

using X::foo;