2012-08-14 126 views
2

有一個基類A,這是虛擬的dynamic_cast:應在這種情況下

class A 
{ 
    ~virtual A() = 0; 
}; 

更派生類B,C,d,E代替...

class B : public A 
{ 
}; 

class C: public A 
{ 
}; 

和類似其他入級d,E ......我們有一個指針

std::list <A*> a_list; 

我們刪除其類型是未知的任何元素的列表,例如

A *a = a_list.front(); 

基礎上尖銳的物體,我們決定,做什麼類型......還有更多的可能性,如何做到這一點:

A)dynamic_cast的情況下

的重鑄到派生類型。

if (dynamic_cast <B*> (a)) 
{ 
    //do something (but nothing with a) 
} 

else if (dynamic_cast <C*> (a)) 
{ 
    //do other (but nothing with a) 
} 

但是dynamic_cast的用法表示糟糕的設計。

B)其它屬性

一些aditional的屬性,例如一個對象ID impelemented;

class A 
{ 
    virtual ~A() = 0; 
    virtual short getID() = 0; 
}; 

class B : public A 
{ 
    virtual short getID() {return 1;} 
}; 

class C: public A 
{ 
    virtual short getID() {return 2;} 
}; 

所以修改後的條件

switch (a->getID()) 
{ 
    case 1: // do something (but nothing with a) 
    case 2: // do other (but nothing with a) 
} 

的說明:

我們不與對象直接執行任何操作,但它的類型的基礎上,我們做了一些不同的計算。

問題:

1)它的情況下,我們應該避免的dynamic_cast?

2)是否有任何優先解決方案(可能會有所不同)?

感謝您的幫助。

+0

我想,你可以在這裏實現[訪問者模式](http://en.wikipedia.org/wiki/Visitor_pattern) – Lol4t0 2012-08-14 20:08:54

回答

4

根據C項目90 ++編碼標準(Amazon):避免型開關(無論是否帶有if-else梯和dynamic_cast,或switch語句getID()功能做到這一點)。偏愛不是通過虛函數

class A 
{ 
public: 
    ~virtual A() = 0; 

    void fun() // non-virtual 
    { 
    // main algorithm here 

    // post-processing step 
    post_fun(); 
    } 

    virtual void post_fun() = 0; 
}; 

class B : public A 
{ 
public: 
    virtual void post_fun() { /* bla */ } 
}; 

class C: public A 
{ 
public: 
    virtual void post_fun() { /* meow */ } 
}; 

A* a = a_list.front(); 
a->fun(); // will be resolved at run-time to whatever type a points to 

其原因是具有顯式的類型開關難以維護和更新依靠多態性。如果您從A獲得新派生類,則需要更新遍歷類型的每個位置。相反,如果你依賴於虛擬函數,編譯器會自動爲你做。

+0

_never_非常強大,但是類型切換合適(或必要)的情況很少,遠在之間。 – Chad 2012-08-14 20:00:18

+0

@Chad tnx,已更新! – TemplateRex 2012-08-14 20:02:20

+0

@ rhalbersma但我不想直接對分析的對象進行任何操作。因此,虛擬函數getID()僅適用於區分對象類型。我認爲,這是一個有點不同的情況。 – justik 2012-08-14 20:05:23

0

在大多數情況下,當你需要使用任何B特異性(保持你的命名),你應該存儲B *(或shared_ptr<B>,相當),而不是A *。在所有其他情況下,隱藏所有背後的多態性。

考慮以下層次:

class Animal 
{ 
public: 
    Animal() {} 
    virtual ~Animal() = 0; 
    virtual void Breathe(); 
}; 

class Bird : public Animal 
{ 
public: 
    Bird() {} 
    virtual ~Bird() {} 
    virtual void Breathe() {...} 
    virtual void Fly() {...} 
}; 

,想象你存儲Animal *秒 - 你不應該現在就Fly()。如果您需要打電話,請從頭開始存儲Bird *。但是所有的動物都必須呼吸 - 這就是爲什麼這個函數是從基類繼承而來的。


概括起來:如果你需要做的事情Child特異性,存儲指針Child,不Base

0

通常情況下,執行動態轉換以獲取特定類型的對象來處理它們。

struct base 
{ 
    virtual void a() = 0; 
}; 

struct foo : base 
{ 
    virtual void a() { ... } 
    void specificFooMethod(); 
}; 

struct bar : base 
{ 
    virtual void a() { ... } 
    void specificBarMethod(); 
}; 

base * pBase = ...; 
pBase->a(); // no casting required here 
if (foo * p = dynamic_cast<foo*>(pBase)) 
{ 
    p->specificFooMethod(); 
} 
else if (bar * p = dynamic_cast<bar*>(pBase)) 
{ 
    p->specificBarMethod(); 
} 

特定的*方法不是虛擬的,並且不需要通過基接口進行處理而不需要轉換。因此dynamic_cast應該用於當您確實需要特定類型的對象時,該對象具有不能移動到基類中的抽象層的附加功能/屬性。