2013-07-11 48 views
3

我確定我錯過了部分代碼。無法在C++中調用派生類函數

我有以下代碼:

#include <iostream> 

using namespace std; 

class Record 
{ 
private: 
    int age; 
    string name; 
public: 
    virtual int getType()=0; 
}; 


class Student: public Record 
{ 
    private: 
    int level_; 
    public: 
    Student() 
    { 
     level_=1; 
    }; 
    ~Student() {}; 
    int getType() 
    { 
     return 1; 
    } 

    int level() 
    { 
     return level_; 
    } 
}; 


int main (int argc, char ** argv) 
{ 
    Record *r = new Student(); 
    cout <<"tuype " << r->getType(); 
    cout <<"Class " << r->level(); 
} 

的問題是:爲什麼我無法調用r->level()?需要進行哪些更改才能調用它?

+3

你'Record'類應該有虛析構函數。你的Student類應該有一個隱式定義的類。並且你的所有函數都不需要分號。 – chris

+1

我認爲有一個getType()方法通常不是個好主意。這並不總是一件壞事,但如果可能的話儘量避免它。不要忘了爲繼承設計的類中的虛擬析構函數,對於虛擬析構函數的建議,這非常重要 – SpongeBobFan

+0

@chris:+1,但是'Student'是否具有隱式定義的析構函數並不重要。分號,*顫抖* ;-)。 –

回答

8

更改記錄,使level()虛擬

您寫道:

Record *r = new Student(); 

該行後,編譯器認爲r是一個指針到任何一個Record或者一些Record - 派生類(它是),但它只知道爲Record指定的接口。在Record中沒有virtuallevel()功能,所以不能通過Record接口訪問Student的關卡功能。只需添加這樣的功能Record,你會沒事:

virtual int level() { return 0; } // Student may override implementation 

virtual int level() = 0; // Student MUST override implementation 

一種替代方案:檢查是否記錄*解決了學生

我上面說的。 ..

沒有virtuallevel()功能Record,所以不能通過Record界面訪問Student的關卡功能。

...並告訴你如何可以添加到Record接口,但另一種方法是重新獲得訪問Student接口,如:

if (Student* p = dynamic_cast<Student*>(r)) 
    std::cout << "Level " << p->level() << '\n'; 

第一線檢查是否Record* r碰巧指向一個Student(當然在你的代碼中它總是這樣,但是想象你在一個接受Record*的函數中,或者正在循環一個這樣的指針的容器,其中一些真的是Students而其他的不是)。如果是這樣,返回的指針可用於訪問它作爲Student對象,具有任何額外的功能/成員可用(和潛在的限制,如果某些Record功能隱藏在某種方式)。

這種方法一般是令人難以接受的,因爲它引出了一個問題:「爲什麼我們進行治療StudentRecord,如果我們需要知道的‘水平’等Record派生類型甚至沒有等級的概念? 」。不過,有時候會發生類似的情況。添加virtuallevel功能到Record如果Student是(其中一個)它將具有有意義的值的唯一派生類,那麼它並不理想:這就是所謂的胖接口 - 您會發現幾個討論如果您有副本,則可以使用C++編程語言。

(sasha.sochka的回答是首先要提到的dynamic_cast選項 - 請給予好評)

基類應該有虛析構

按克里斯的評論,您應該添加到Record

virtual ~Record() { } 

這確保派生類的析構函數實現在派生對象爲delete時被調用d使用基於類的函數,類指針(例如,如果在main()的底部添加delete r;)。 (編譯器仍然會確保之後調用基類析構函數)。

這是未定義的行爲,如果你不這樣做,最好你會發現在派生類中添加的任何額外的數據成員沒有他們的數據成員調用......對於int是無害的,但對於說std::string它可能會泄漏內存,甚至不能鎖定程序以後掛起。當然,依賴最好的未定義行爲並不是一個好主意;-)但是我認爲理解什麼是絕對不會發生的,除非你創建了基礎析構函數virtual。在學生

推薦小改進,如果您levelvirtualRecord,然後使其更清晰的Student類的讀者,level()是從基類virtual功能的實現,可以使用override關鍵字,如果你有適當C++ 11功能的編譯器:

int level() override 
{ 
    return level_; 
} 

這會給你一個編譯器錯誤,如果它找不到匹配(非constvirtual int level()在基類,所以它可以避免一些偶爾的故障排除。如果您覺得它具有文檔值(對於C++ 03來說特別好(其中override不是一種選項),但您可能還會重複使用virtual關鍵字),但它不會產生任何功能差異 - 只要它的功能保持爲virtual(隱式或顯式地)從基類中覆蓋虛擬函數。

+0

好的答案,但我認爲作者想在這裏使用'Student * r = new Student()',因爲'level()'方法對於'Student'是特定的,對於另一個'Record'子類可能無用 – SpongeBobFan

+0

@Tony,感謝您的解釋,這意味着,派生類函數不能被調用,除非它在給定場景中的基類中指定了嗎? – Whoami

+0

@Whoami:只要你把對象當作'Record',對。我將添加一節討論如何檢查'Record *'是否恰好指向'Student',因此即使沒有通過基類接口公開,也可以訪問'level()'函數。儘管儘可能避免了這種技術,但最好儘可能避免這種技術,但是在基類中包含虛擬函數時,也就是所謂的「胖」接口也是一種冒險,只有一部分派生類具有有意義的實現(無所作爲的實現可能仍然有意義)。將解釋。 –

1

您在Record中缺少virtual method level()聲明。注意,爲了防止資源泄漏,你需要在Record中定義虛擬析構函數。

class Record 
{ 
... 
public: 
    virtual ~Record() {} 
    virtual int level() = 0; 
    virtual int getType() = 0; 
}; 
2

由於Record類一無所知一個名爲level()

你需要做一個虛擬level()函數在基類的功能。

2

請在基類的虛函數一樣

virtual int level() = 0; 

一旦你在基類中記錄創建虛擬功能level(),有必要對學生有它的學生類功能level()。目前,您在類Record中沒有虛擬level()函數,因此您無法使用基類Record以訪問學生類的level()函數。

0

編譯時,你應該得到類似的錯誤消息:

error C2039: 'level' : is not a member of 'Record' 

您需要在您的記錄類補充一點:

virtual int level()=0; 
3

你不能叫r->level()因爲你試圖調用類Record中不存在的函數。在通過r指出您的具體情況數據不僅Record但在同一時間Student,所以你可以選擇:

Student *r = new Student(); 
cout <<"tuype " << r->getType(); 
cout <<"Class " << r->level() 

Record *r = new Student(); 
cout <<"tuype " << r->getType(); 
cout <<"Class " << dynamic_cast<Student*>(r)->level() 

如果希望所有Record s到有級別,您可以添加純虛函數而無需執行。但是,如果你這樣做,你將無法創建Record類的instantate對象(但你可以instatntiate這是少兒班):

class Record { ... 
    virtual int level() = 0; 
} 

另一個問題是:你應該在Record類標記您的析構函數爲虛擬的,因爲當你會的Studentdelete r構造函數將不會被調用

+0

對於頂端的「可以選擇:」代碼,你的意思是「Record * r = new Student();'成爲'Student * r = new Student();'?它仍然是壞的。 +1關於可能使用'dynamic_cast <>'的點。 –

+0

@TonyD,是的,我的意思是完全一樣,謝謝注意。 –

1

學生實例由upcasted以實錄:

Record *r = new Student(); 

雖然持有學生例如,R代表作爲記錄。使用C++的多態性機制將r-> getType()函數調用綁定到student :: getType。 要調用級別()函數,您可以:

 
1. Add a virtual function level() to Record class. 
2. downcasting r to Student class, as follows: 
Student *r_new = dynamic_cast<Student>(r); 
r_new->level(); 
相關問題