2017-08-01 25 views
0

我從Base類繼承來創建兩個不同的派生類(Derived1和Derived2),然後將它們放入一個向量中。假設我想根據類的類型調用派生類的函數。如何從包含基類指針的容器調用派生類函數(基於它的類型)?

僞代碼:

if holder[1] stored Derived1 then I want to call GetZ() 
else if holder[1] stored Derived2 then I want to GetFlag(). 

嘗試:

#include <iostream> 
#include <memory> 
#include <vector> 

class Base { 
public: 
    Base(int x, int y) : x_(x), y_(y) {} 

    int GetX() { return x_; } 
    int GetY() { return y_; } 

private: 
    int x_; 
    int y_; 
}; 

class Derived1 : public Base { 
public: 
    Derived1(int x, int y, int z) : Base(x, y), z_(z) {} 

    int GetZ() { return z_; } 

private: 
    int z_; 
}; 

class Derived2 : public Base { 
public: 
    Derived2(int x, int y, bool flag) : Base(x, y), flag_(flag) {} 

    bool GetFlag() { return flag_; } 

private: 
    bool flag_; 
}; 

std::vector<std::shared_ptr<Base>> holder; 
void print(); 
int main() { 
    holder.push_back(std::make_shared<Derived1>(3, 4, 5)); 
    holder.push_back(std::make_shared<Derived2>(6, 7, true)); 

    print(); 
} 

void print(){ 

    for(auto& it : holder){ 
    // call this if "it" is Derived2 
    // else call it->GetX() 
    // currently this gives compilation error 
    // because of object slicing 
    std::cout << it->GetFlag() << std::endl; 
    } 

} 
+1

如果你不能以一種他們提供類似接口的方式編寫你的類,那麼多態可能不是最好的方法。 – patatahooligan

+0

另一種可能性是訪客模式。 – Jarod42

回答

4
for(auto& it : holder){ 
    if (auto* D1 = dynamic_cast<Derived1*>(it->get())) { 
    std::cout << D1->GetZ(); 
    } else if (auto* D2 = dynamic_cast<Derived2*>(it->get())) { 
    std::cout << D2->GetFlag(); 
    } 
    std::cout << std::endl; 
} 

動態投射通常是代碼異味,證明您的界面Base缺少功能。一旦你動態投射,你的界面就會從Base所說的內容到你整個類型的規劃和內容。

相反,添加:

virtual boost::optional<int> GetZ() { return {}; } 
virtual boost::optional<bool> GetFlag() { return {}; } 

Base和覆蓋的。

for(auto& it : holder){ 
    if (auto Z = it->GetZ()) { 
    std::cout << *Z; 
    } else if (auto flag = it->GetFlag()) 
    std::cout << *flag; 
    } 
    std::cout << std::endl; 
} 

現在,我們不再關心我們用來實現ZFlag哪些具體派生類型。

this SO answerlink to a reference std::optional implementation使用升壓軟件許可證,並且是一個頭文件。

+0

這可以做到不使用提升? –

+0

@vantamula是的,你可以自己寫一個'optional'的實現。或者得到C++ 17'std :: optional'。或從某個地方找到一個。 – Yakk

+0

我知道這個問題是一個小題目..但你能指出我在正確的方向實施我自己的'可選'。 –

1

檢查好ol dynamic_cast伎倆 - 如果它是澆注料,這是正確的類型。

無論如何,我建議使用而不是使用此設計模式(檢查運行時的類型並根據此類型來決定),但將邏輯放入派生類中;有一個共同的方法,要麼做一個或其他的事情:

class Base { 
public: 
    Base(int x, int y) : x_(x), y_(y) {} 

    int GetX() { return x_; } 
    int GetY() { return y_; } 

    virtual int do_the_right_thing(); 

private: 
    int x_; 
    int y_; 
}; 

class Derived1 : public Base { 
public: 
    Derived1(int x, int y, int z) : Base(x, y), z_(z) {} 

    int GetZ() { return z_; } 

    virtual int do_the_right_thing() { return GetZ() }; 

private: 
    int z_; 
}; 

class Derived2 : public Base { 
public: 
    Derived2(int x, int y, bool flag) : Base(x, y), flag_(flag) {} 

    bool GetFlag() { return flag_; } 

    virtual int do_the_right_thing() { return GetFlag() }; 

private: 
    bool flag_; 
}; 


void print(){ 

    for(auto& it : holder){ 
    // call this if "it" is Derived2 
    // else call it->GetX() 
    // currently this gives compilation error 
    // because of object slicing 
    std::cout << it->do_the_right_thing() << std::endl; 
    } 

} 

與此交易將模板和類型特徵的STL式的方式 - 但我個人認爲這是一個滋擾。

+0

它是寫入返回int而'返回GetFlag()',因爲它的類型爲'bool'? –

+0

'GetFlag()'返回一個'bool',它不是'int'。例如,如果'boolalpha!= 0','bool'流媒體將按字母順序打印出來。你的'do_the_right_thing'可以說是一個'std :: ostream&'。 – Yakk

+0

這確實很有爭議:)是的,你是對的,但方法名稱表明我認爲在這裏失蹤:OP的考慮他們想要派生類做什麼!所以,我非常感謝你發現我的錯誤,但是再次,OP要根據應用程序的目的定義一個合理的界面。 –

相關問題