2014-03-06 57 views
1

的同事,今天問我關於代碼看起來有點像這樣:抽象基類調用父的純虛函數

#include <iostream> 

template <class T> 
class IBase { 
public: 
    virtual ~IBase() {} 

public: 
    virtual void foo() = 0; 
}; 

template <class T> 
class Base : public IBase<T> { 
public: 
    virtual void bar() { 
     foo(); // compiler error 
    } 
}; 

class Derived : public Base<int> { 
public: 
    virtual void foo() { 
     std::cout << "Hello World!\n"; 
    } 
}; 

int main() { 
    Derived d; 
    d.bar(); 
} 

起初他得到一個編譯器錯誤說「foo()」沒有被發現。好的,所以他試圖將其更改爲IBase<T>::foo();。雖然編譯,它導致鏈接器錯誤。所以我馬上回憶起,之前我看到過這種類型的問題,並建議他改爲寫this->foo();。中提琴!問題解決了!

然後他問我爲什麼不明白foo();工作是不是this->x();基本相同,x();?老實說,我不知道,但他激怒了我的興趣。所以,我們在這裏:

總結:

virtual void bar() { 
    this->foo();  // works 
    //IBase<T>::foo(); // linker error 
    //foo();   // compiler error 
} 

問題是爲什麼需要this->。爲什麼其他選項不起作用?

回答

3

因爲基類成員是一個從屬名稱 - 它的含義取決於模板參數,所以在模板實例化之前不知道。該名稱在通用IBase模板中未查找,因爲在實例化之前可能會專門給它賦予不同的含義。

IBase<T>::限定它調用基類函數非幾乎;這通常不是你想要的,特別是如果(在這裏)它是一個沒有實現的純虛函數。因此,當你嘗試這個鏈接器錯誤。

this->限定它告訴它的成員的編譯器,任何進一步的檢查被推遲到模板實例。該功能仍然被稱爲虛擬。

+1

但'Base ::'也禁止虛擬調用語義,這就是爲什麼存在鏈接器錯誤。 –

+0

@SebastianRedl:好點,我沒有想到通過正確。 –

2

想象一下,你是編譯器。您剛剛閱讀並編譯代碼,現在您已達到bar函數。在這個功能中,你會看到有人試圖做foo()。在這一點上,你知道foo是什麼嗎?你沒有。它可能來自基類,但你不可能知道,因爲你不知道什麼是T。當然可能有IBase專業化,其中foo是完全不同的東西。

當你的函數調用之前堅持this->,它會導致編譯器把它作爲一個從屬名稱。這意味着,編譯器會說:「好吧,這取決於this的類型,我還不知道。我會等待,直到後來,當我知道類是如何被實例化,之前我找foo。」

IBase<T>::foo();給出了一個鏈接器錯誤,因爲fooIBase<T>中根本沒有定義。

2
#include <iostream> 

template <class T> 
class IBase { 
public: 
    virtual ~IBase() {} 

public: 
    virtual void foo() = 0; 
}; 

int foo() { std::cout << "hello!\n"; } 
template <class T> 
class Base : public IBase<T> { 
public: 
    virtual void bar() { 
    foo(); // which foo?! 
    } 
}; 
template <> 
class IBase<int> { 
public: 
    virtual ~IBase() {} 
//virtual void foo() = 0; -- no foo()! 
}; 

class Derived : public Base<int> { 
public: 
    virtual void foo() { 
    std::cout << "Hello World!\n"; 
    } 
}; 

int main() { 
    Derived d; 
    d.bar(); 
} 

以上說明了爲什麼C++不允許隱式地找到依賴父類型的成員。

當您撥打foo()Base,應該調用哪個foo()IBase<T>或免費功能foo()

要麼我們把決定關閉,直到後來,或者我們去免費功能foo()

如果我們只使用自由函數foo()(如果有一個可見),那麼#include順序中的細微更改可以大大改變程序的功能。因此,如果它應該調用免費功能foo(),它必須錯誤,如果沒有找到,或者我們完全擰緊。

如果我們推遲到稍後的決定,這意味着template可以解析和理解,直到更晚的日期。這將更多的錯誤移動到實例化的地步。這也會導致一些令人驚訝的行爲,例如在上述情況下,有人可能會認爲「我打電話給方法foo()」,但實際上最終會調用免費功能foo()而不進行診斷。