2016-08-23 77 views
2

當試圖訪問派生類行爲時,我讀到的最常見方法是使用dynamic_cast s,即dynamic_cast<DerivedA*>(BasePtr)->DerivedAOnlyMethod()。這不太好,但每個人都明白髮生了什麼。dynamic_cast vs基於虛擬AsDerived方法

現在我正在此轉換由導出基類,對於每一個派生類的虛函數處理的代碼,即:

class Base 
{ 
public: 
    virtual DerivedA* AsDerivedA() { throw Exception("Not an A"); } 
    virtual DerivedB* AsDerivedB() { throw Exception("Not a B"); } 
    // etc. 
}; 
class DerivedA : public Base 
{ 
public: 
    DerivedA* AsDerivedA() { return this; } 
}; 
// etc. 

使用是那麼BasePtr->AsDerivedA()->DerivedAOnlyMethod()。 Imho,這使基類變得混亂,並暴露出它不應該需要的派生類的知識。

我太缺乏經驗,肯定地說哪個更好,所以我正在尋找爭論和反對任何構造。哪一種比較習慣?他們如何比較性能和安全性?

+3

這些函數打破[open closed principle] [1]。如果您要添加C類,則需要更改基類以添加AsDerivedC。 [1]:https://en.wikipedia.org/wiki/Open/closed_principle – Davidbrcz

回答

2

好,把[email protected] - 方法爲基礎類肯定會導致潛在更快的鑄造。

如果使用final來限制繼承層次結構,那麼優點可能會減少或刪除。

此外,你是正確的,因爲它引入了混亂,它引入了所有相關的派生類到基類的知識是不常見的。

總之,可能有時是有用的一個瓶頸,但你爲可憎工資。

1

你的直覺是對的,AsDerivedX方法是混亂的。事實上,在運行時可以檢查這些虛擬函數是否過載相當於類型檢查的代價。所以,在我看來,這樣的C++的方法是:

void doSomething(Base *unsureWhichAorB) { 
    DerivedA *dA = dynamic_cast<DerivedA*>(unsureWhichAorB); 
    if(dA) //if the dynamic cast failed, then dA would be 0 
     dA->DerivedAOnlyMethod(); 
} 

注意,對於dA非zeroness的檢查是關鍵在這裏。

2

沒有看到更多的代碼,很難提供太多的建議。然而,需要知道你所調用的對象的類型更多地是針對變體而不是多態的類型。

多態性是關於信息隱藏。來電者不需要知道他拿着什麼類型。

這樣的事情,也許?

struct base 
{ 
    virtual bool can_do_x() const { return false; } 
    virtual void do_x() { throw std::runtime_error("can't"); } 
    virtual ~base() = default; 
}; 

struct derived_a : base 
{ 
    virtual bool can_do_x() const { return true; } 
    virtual void do_x() { std::cout << "no problem!"; } 
}; 

int main() 
{ 
    std::unique_ptr<base> p = std::make_unique<derived_a>(); 
    if (p->can_do_x()) { 
    p->do_x(); 
    } 
} 

現在我們正在討論的是對象的能力而不是類型。

1

你完全正確的認爲,這樣的解決方案不僅使基類混亂,而且對其不必要的依賴。在一個乾淨的設計中,基類不需要,實際上不應該知道什麼關於它的派生類。其他一切都將很快成爲維護噩夢。

但是,我想指出我在「儘量避免dynamic_cast」 - 團隊。這意味着我經常看到dynamic_cast,這可以通過適當的設計來避免。所以首先要問的問題是:爲什麼我需要知道派生類型?通常要麼通過正確使用多態性來解決問題,要麼「丟失」已經是錯誤路徑的類型信息。

喜歡使用多態,而不是dynamic_cast

class Base 
{ 
public: 
    virtual void doSomething() = 0; 
}; 

class DerivedA : public Base 
{ 
public: 
    void doSomething() override { //do something the DerivedA-way }; 
}; 

class DerivedB : public Base 
{ 
public: 
    void doSomething() override { //do something the DerivedB-way }; 
}; 
// etc. 
+0

如果所需函數在行爲中是多態的,那麼它們就是基類中的虛函數。但有時你需要派生類的具體方法。 – Psirus

+1

然後我會質疑決定將它們分配給'Base'類型的變量。如果你 - 在某種程度上 - 需要知道實際類型,你爲什麼「首先把它扔掉」? – sigy

+0

將它們存儲在普通容器中。 – Psirus