2011-07-17 45 views
16

我遇到了C++繼承的問題。C++隱藏虛函數

我有一個類層次結構:

class A { 
public: 
    virtual void onFoo() {} 
    virtual void onFoo(int i) {} 
}; 

class B : public A { 
public: 
    virtual void onFoo(int i) {} 
}; 

class C : public B { 
}; 


int main() { 
    C* c = new C(); 
    c->onFoo(); //Compile error - doesn't exist 
} 

我的問題是:爲什麼不這樣編譯?我的理解是C應該從A繼承兩個onFoo函數 - 事實上,如果您刪除了B中onFoo的重新定義,則編譯 - 但g ++提供的錯誤是C沒有onFoo()函數。

回答

33

您遇到有關名稱查找用C如何工作++的問題。特別是,在解析成員時,編譯器將查看成員正在訪問的對象的靜態類型。如果在該類中找到標識符,則查找完成並且(在成員函數的情況下)重載解析開始。如果找不到標識符,它將逐級爬取層次結構,嘗試在時間一級定位標識符

在您的具體情況下,您有c->onFoo();cC類型。編譯器在C中看不到onFoo的任何聲明,因此它在層次結構中繼續向上。當編譯器檢查B時,它看到在該級別有一個void onFoo(int i)的聲明,因此它停止查找,並嘗試重載解析。此時,由於參數不一致,重載解析失敗。

事實上的void onFoo(int)聲明存在於B等級具有隱藏的重載的其餘在任何基類,因爲它會停止查找的效果。請注意,這是一個非限定查找的問題,函數仍然存在並適用於該對象,但不會通過常規查找找到(您仍然可以將其稱爲c->A::onFoo())。

至於如何處理與隱藏,最簡單的方法是通過採用using聲明帶來的功能集成到範圍:

class B : A { 
public: 
    using A::onFoo; // All A::onFoo overloads are *considered* here 
    void onFoo(int); 
}; 

這裏using聲明的作用是,當B類在搜索onFoo標識符時,編譯器被指示還考慮在基類中的所有過載onFoo,使定期查找能夠找到A::onFoo()

+0

+1 - 很好的解釋。 – Mahesh

7

這是名稱隱藏,基本上只有聲明的覆蓋存在於B中,並且A中的其他重載是隱藏的。

2

A類和B類的方法應該公開。那就是,你在每個類聲明的末尾都缺少分號。

class A { 
public: 
    virtual void onFoo() {} 
    virtual void onFoo(int i) {} 
}; 

class B : public A { 
public: 
    virtual void onFoo(int i) {} 
}; 

class C : public B { 
}; 


int main() { 
    C* c = new C(); 
    c->onFoo(); //Compile error - doesn't exist 
} 
+0

這些都是有效的點,但是你的「修復」也不能編譯。 –

1

你忘public:修改在兩個A類方法和B之前因此方法onFoo是私有的,因此不可見這些類以外的任何地方。

+0

哎呀 - 這不是問題,那只是我抄錄錯誤的問題。編輯修復 –

12

如果你想基類成員重載派生類中的成員,要使用using

struct A 
{ 
    virtual void onFoo() {} 
    virtual void onFoo(int i) {} 
}; 

struct B : A 
{ 
    using A::onFoo; 
    virtual void onFoo(int i) {} 
}; 

struct C : B 
{ 
}; 


int main() 
{ 
    C* c = new C(); 
    c->onFoo(); 
} 
-2

我猜你已經錯過了在class B添加此:

struct B : A 
{ 
    using A::onFoo; 
    virtual void onFoo(int i) {} 
    void onFoo() {} //This line missing in your code. 
}; 

現在這個編譯!