2017-06-29 36 views
1

我試圖實現一個簡單的文件系統。我有一個基類入口。我有兩個從名爲File和Directory的條目繼承的類。目錄包含一個Entry對象列表。我有一個稱爲changeDir的目錄和文件,但不是條目的方法。我希望我可以在Entry對象上調用這個方法,並且編譯器會根據Entry是Directory還是File來知道使用哪種方法。任何方式來完成這個?C++中的多態性。在基類上調用繼承的方法

class Entry{ 
    public: 
    std::chrono::time_point<std::chrono::system_clock> dateCreated; 
    std::string fileName; 
    Entry(std::string name); 
    ~Entry(); 
}; 

Directory* Directory::changeDir(Directory* dir, Directory* currentDir){ 
    currentDir = dir; 
    return currentDir; 
} 

void File::changeDir(Directory* dir, Directory* currentDir){ 
    std::cout<<"You cannot change directories into a file\n"; 
} 

unordered_set<Entry*> s; 
s.add(File); 
s.add(Directory); 
for each in s: each.changeDir(); //pseudo code 

不幸的是,它告訴我一個changeDir()方法尚未被聲明爲Entry類。我通常會爲Entry類創建一個方法,但它說Directory不在此範圍內聲明(顯然,因爲Directory是Entry的派生類)。我覺得我已經在Python中做過這種類型的多態。在C++中沒有辦法做到嗎?謝謝。

編輯:Set將指針指向Entry對象,而不是實際的Entry對象。

編輯2:看來虛擬類是解決這個特定問題的方法。我相信更好,更強大的解決方案將是讓每個目錄包含一組文件和一組導演而不是一組導演。

+0

如果你把'changeDir'放在基類中,你*可以*做到這一點,但是因爲它不涉及'File'爲什麼要這樣做?另外,如果沒有引用或指針,您將獲得[object slicing](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=3&cad=rja&uact=8&ved=0ahUKEwinvqq60OPUAhXM54MKHedcAdQQFgg0MAI&url=https%3A %2F%2Fen.wikipedia.org%2Fwiki%2FObject_slicing&usg = AFQjCNHH_00F7lDRk2r0g2QEwWX0SJ9wg) – crashmstr

+0

另外,如果'File'和'Directory'是類,則不能將*類添加到您的集合中。 – crashmstr

+0

@crashmstr該方法有時會在File對象上調用,我需要一種方法來處理這種情況。我不能將方法放在基類中,因爲它返回Directory *並將Directory *作爲參數。編譯器狀態目錄未在該範圍內聲明。我不確定你的評論是什麼意思。你的意思是我的設置只能使用Entry對象嗎?不是目錄和文件技術上的入口對象? – user3736114

回答

4

您需要virtual methods

struct Entry 
{ 
    virtual void changeDir() 
    { 
     std::cout << "Entry" << std::endl; 
    } 
}; 

struct File : Entry 
{ 
    virtual void changeDir() override 
    { 
     std::cout << "File" << std::endl; 
    } 
}; 

struct Directory : Entry 
{ 
    virtual void changeDir() override 
    { 
     std::cout << "Directory" << std::endl; 
    } 
}; 

不過,如果你的收藏是平原Entry對象,slicing會破壞你所有的努力:

int main() 
{ 
    std::vector<Entry> entries; 

    entries.push_back(File { }); 
    entries.push_back(Directory { }); 

    entries[ 0 ].changeDir(); // > Entry 
    entries[ 1 ].changeDir(); // > Entry 

爲了保持正確的對象,您必須動態分配它們並存儲某種指針:

std::vector< std::unique_ptr<Entry> > entryPointers; 

    entryPointers.push_back(std::make_unique<File>()); 
    entryPointers.push_back(std::make_unique<Directory>()); 

    entryPointers[ 0 ]->changeDir(); // > File 
    entryPointers[ 1 ]->changeDir(); // > Directory 
} 

幾點:

  • 不要忘了override specifier壓倒一切的時候 - 這將確保你真的重寫基類的方法,而不是僅僅宣佈一個新的;
  • 您可以使用原始指針而不是unique_ptr,但這是非常不推薦的,因爲您必須自己釋放它們以避免內存泄漏;
  • 儘管有效,但儘可能避免在界面中使用虛擬方法。例如參見這個基本的Herb Sutter's article
+0

感謝您的詳細解釋。這似乎解決了編譯器錯誤。 – user3736114

1

您可以使方法changedir成爲一個純虛函數,然後通過實例化Base類對象的指針或引用(它將指向文件或目錄)然後調用該函數並通過多態性來實現所需的行爲將被正確調用。 [對象切片]

1

只要你僅指指針或引用目錄,你可以向前它聲明:

class Directory; 
class Entry { 
    ... 
    virtual Directory* changeDir(Directory* dir, Directory* currentDir) = 0; 
}; 
1

我認爲,如果你想打電話changeDir對整個集,你應該確定所有元素都是Directory的實例(因爲它只對它們有意義),並聲明該集合包含目錄,而不是條目。

另一方面,如果你的設置可能包含兩種類型的條目,你不應該試圖在它們上面調用一個可能未定義的方法。