2014-09-26 33 views
0

我想實現一個函數來比較派生類。但是我發現我必須將基類對象轉換爲派生類對象。如何實現一個虛函數equal()

有沒有辦法避免使用強制轉換或其他更好的設計來實現比較功能?

class A { 
    public : 
     virtual bool equal(const A & obj) const = 0; 
}; 

class AA : public A{ 
    public: 
     AA (int i) : m_i(i) {} 
     virtual bool equal(const A & obj) const 
     { 
      return m_i == dynamic_cast<const AA&>(obj).m_i; 
     } 

    private: 
     int m_i; 
}; 

int main() { 
    AA aa1(10), aa2(9); 
    A &a1 = aa1, &a2 = aa2; 

    a1.equal(a2); 

    return 0; 
} 
+2

請注意,引用類型的'dynamic_cast'可能會引發異常。可能會更好地轉換爲指針,如果指針返回NULL,您可能只想返回「false」。 – 2014-09-26 20:53:48

+0

也許使用在成員變量'm_i'的基類中虛擬的getter? – abiessu 2014-09-26 20:53:54

+0

閱讀關於雙派遣和關於它的古典解決方案 - 模式訪問者。 – Ilya 2014-09-26 20:56:55

回答

0

我在自己的代碼中多次被這個請求咬了。我將介紹一個簡單的例子:

struct Fruit 
{ 
    virtual bool is_equal(Fruit const & f) const = 0; // Compare two fruits 
    // Some dangerous actions: 
    bool operator==Fruit const & f) 
    { 
    return is_equal(f); // Dispatch! 
    } 
}; 

struct Strawberry : public Fruit 
{ 
    bool is_equal(Fruit const & f) 
    { 
    bool equal = false; 
    // The f could be any fruit, such as tomatoes or pineapples or bananas. 
    // Need to perform a dynamic_cast to verify that the f is a strawberry. 
    Strawberry const & s = dynamic_cast<Strawberry const &>(f); 
    // perform strawberry comparison; 
    return equal; 
    }; 
} 

struct Banana : public Fruit 
{ 
    bool is_equal(Fruit const & f) 
    { 
    bool equal = false; 
    // The f could be any fruit, such as tomatoes or pineapples or strawberries. 
    // Need to perform a dynamic_cast to verify that the f is a banana. 
    Banana const & b = dynamic_cast<Banana const &>(f); 
    // perform banana comparison; 
    return equal; 
    }; 
} 

bool Compare_Fruits(Fruit const * pf1, Fruit const * pf2) 
{ 
    if (*pf1 == *pf2) 
    { 
    cout << "Fruits are equal\n"; 
    return true; 
    } 
    cout << "Fruits are different\n"; 
    return false; 
} 

int main(void) 
{ 
    // Fun with fruit. 
    Fruit * p_fruit_1 = new Strawberry; 
    Fruit * p_fruit_2 = new Banana; 
    Fruit * p_fruit_3 = new Strawberry; 

    // Is this valid, comparing two different fruits, when 
    // we just want to compare two strawberries? 
    if (Compare_Fruits(p_fruit_1, p_fruit_3)) // OK, both are strawberries 
    { 
    // ... 
    } 
    if (Compare_Fruits(p_fruit_1, p_fruit_2)) // Not OK, two different fruits. 
    { 
    // ... 
    } 
    return 0; 
} 

這裏的關鍵是,如果要實現在基類中的相同操作,使後代可以比較的情況下,你做了個錯誤的決定。請注意,我可以將指向一個後代(草莓)的實例的指針傳遞給另一個後代(香蕉)的比較方法,因爲相等函數基於指向基類的指針。不幸的是,沒有辦法知道派生自基類的後代有多少或全部。

對於編程安全性,我強烈建議不要將虛擬比較運算符放在基類中。基類應該有比較運算符,聲明爲protected,只比較基礎數據成員。這種方法將由後代調用。

+0

然後如何如果你有一對只有基類指針或引用的對象,你會知道它們是否相等? – 2014-09-26 21:33:17

+0

如果通過基類指針或引用已知的兩個對象只能使用基類的數據進行比較。只有當兩個對象是同一類的實例時,才能比較內容相等性。同樣,當基類是「Life_Form」,一個實例是樹而另一個實例是蜘蛛時,正在比較什麼?這沒有意義。在我的項目中,Field是一個基類,我有一個'Record',它是'Field'的一個容器。我如何確定我沒有將'Field :: Text'與'Field :: Double'進行比較? – 2014-09-27 00:14:43

+0

在問題中顯示的虛擬比較函數中使用'dynamic_cast',您可以輕鬆地將比較函數放在基類上 - 您無法在其中實現*函數。 – 2014-09-27 00:41:35