2009-11-19 81 views
10

可能重複:
What’s the right way to overload operator== for a class hierarchy?相等測試在C派生類++

在C++中,怎樣才能派生類覆蓋了基類的平等測試以有意義的方式?

例如,假設我有一個基類A.類B和C從A.現在給定的兩個指針給兩個A的對象派生,我可以測試它們是否相等(包括任何亞類的數據)?

class A { 
    public: int data; 
}; 

class B : public A { 
    public: float more_data; bool something_else; 
}; 

class C : public A { 
    public: double more_data; 
}; 


    A* one = new B; 
    A* two = new B; 
    A* three = new C; 

    //How can I test if one, two, or three are equal 
    //including any derived class data? 

有沒有乾淨的做法呢?我最好的選擇是什麼?

謝謝!

+2

可能的重複:http://stackoverflow.com/questions/1691007/whats-the-right-way-to-overload-operator-for-a-class-hierarchy –

+0

你想比較'T == T ',其中'T'可能是'A','B'或'C'(罰款),或者你想用'C'和'B'將'A'與'B'和'A'進行比較與'C'(可疑)? – sbi

+0

在上面的例子中,我想比較一與二和三​​。他們都是A指針。 – Imbue

回答

12

看完記得公共非虛擬/非公有制虛擬成語及其優點的簡要描述,而不是在那裏。 This wikibook有一個好的描述。

這裏是你如何應用到OP ==:

struct A { 
    virtual ~A() {} 

    int a; 

    friend 
    bool operator==(A const& lhs, A const& rhs) { 
    return lhs.equal_to(rhs); 
    } 
    // http://en.wikipedia.org/wiki/Barton-Nackman_trick 
    // used in a simplified form here 

protected: 
    virtual bool equal_to(A const& other) const { 
    return a == other.a; 
    } 
}; 

struct B : A { 
    int b; 

protected: 
    virtual bool equal_to(A const& other) const { 
    if (B const* p = dynamic_cast<B const*>(&other)) { 
     return A::equal_to(other) && b == p->b; 
    } 
    else { 
     return false; 
    } 
    } 
}; 

struct C : A { 
    int c; 

protected: 
    virtual bool equal_to(A const& other) const { 
    if (C const* p = dynamic_cast<C const*>(&other)) { 
     return A::equal_to(other) && c == p->c; 
    } 
    else { 
     return false; 
    } 
    } 
}; 
1

一個這樣做的方式是使用virtual operator==這需要基類對象作爲參數,以便與不同的派生的對象正常工作。但是,你需要這個功能純虛,從而迫使所有派生對象來實現它。所以你將無法實例化基類。例如:

class A 
{ 
public: 
    virtual ~A(){} 

    //A virtual operator for comparison 
    virtual bool operator==(const A& a) = 0; 

protected: 
    bool compareBase(const A& a); 

private: 
    int m_data; 
}; 

bool A::compareBase(const A &a) 
{ 
    return m_data == a.m_data; 
} 

class B1 : public A 
{ 
public: 

    //Override the base class 
    bool operator==(const A& a); 

private: 
    bool compare(const B1* pB) 
    { 
     if(compareBase(*pB)) 
     { 
      //Code for compare 
      return true; 
     } 

     return false; 
    } 
}; 

bool B1::operator ==(const A &a) 
{ 
    //Make sure that the passed type is same 
    const B1* pB = dynamic_cast<const B1*>(&a); 
    if(pB) 
    { 
     return compare(pB); 
    } 

    return false; 
} 
//Similarly implement for B2 
+0

在基類中擁有一個非純公共虛擬運算符==可能非常危險,因爲對於新的派生類沒有任何保護或警告,它們會忘記覆蓋它,並且如果只有基本部分相等,則最後所有比較相等。 –

+0

是的,你是對的。編輯代碼以使其成爲純虛擬 – Naveen

+0

雖然我同意'A'應該是抽象的,但我不認爲它需要(或應該有)和運算符==。就像「a == b」這樣的表達式,根據「a」和「b」的順序和類型,它們會有很大的不同,但它們可能會是對稱的。 'operator =='對於具有值語義的類型才真正有意義。 –

2

不同的派生類可以使相等的對象嗎?

如果是這樣的:double dispatch是一個選項:它確實需要在基類重載,所以你將不得不依賴

如果沒有:一個解決方案是運營商==()檢查的typeid並返回false如果他們不同。否則,調用一個私有的equals()函數,派生類可以在其中執行static_cast並進行比較。

bool base::operator==(const base& other) const 
{ 
    if (typeid(*this) != typeid(other)) return false; 
    return equal(other); 
} 

bool derived::equal(const base& other) const 
{ 
    derived& derOther = static_cast<derived&>(other); 
    // compare derOther with *this 
    return true; // there is nothing to compare 
} 

這就避免了類型檢查,在所有派生類

+0

這可以防止使用派生類B(比方說B2)與B進行比較(使用問題的層次結構)。 – 2009-11-19 17:56:20

+0

operator ==()僅在基類中定義,因此可以使用層次結構。 equal()函數必須是私有的(如前所述),只能由operator ==() – stefaanv

0

如果你不關心A型的比較,B型或B到C,等等,那麼你可以簡單地實現重載的平等運營商爲每個類:

class A { 
    public: int data; 

    bool operator==(const A& rhs) { 
     return (data == rhs.data); 
    } 
}; 
class B : public A { 
    public: float more_data; bool something_else; 

    bool operator==(const B& rhs) { 
     return (A::operator==(data) && 
       more_data == rhs.more_data && 
       something_else == rhs.something_else); 
    } 
}; 

這是危險的,雖然,因爲如果你從B或C派生新類d,你將有問題。

否則,您需要實施一些比較器,其中包含很多dynamic_cast <> -ing才能真正做到這一點。或者,你可以實現一個函數來爲每個對象創建一個散列碼並利用它,

class A { 
    public: int data; 

    virtual long getHashCode() const { 
     // compute something here for object type A 
    } 

    // virtual here just in case you need to overload it in B or C 
    virtual bool equals(const A& obj) const { 
     return (typeid(*this) == typeid(obj) && 
       getHashCode() == obj->getHashCode()); 
    } 
}; 

class B : public A { 
    public: float more_data; bool something_else; 

    virtual long getHashCode() const { 
     // compute something here for object type B 
    } 
}; 

class C : public A { 
    public: double more_data; 

    virtual long getHashCode() const { 
     // compute something here for object type C 
    } 
}; 

如果您將對象的類型轉換成以某種方式(以上未圖示)的哈希碼那麼你也可以與傻typeid的分配()的比較之上。

0

如果你不介意的基類指的是子類然後雙擊調度:

#include <iostream> 

class B; 
class C; 

class A 
{ 
public: 
    int data; 

    virtual bool equals (const A* rhs) const 
    { 
     std::cout << " A==A "; 
     return data == rhs->data; 
    } 

    virtual bool equals (const B* rhs) const {return false;} 
    virtual bool equals (const C* rhs) const {return false;} 
}; 

class B : public A 
{ 
public: 
    float some_data; 

    virtual bool equals (const A* rhs) const 
    { 
     return rhs->equals (this); 
    } 

    virtual bool equals (const B* rhs) const 
    { 
     std::cout << " B==B "; 
     return A::equals (static_cast<const A*> (rhs)) && some_data == rhs->some_data; 
    } 
}; 

class C : public A 
{ 
public: 
    double more_data; 

    virtual bool equals (const A* rhs) const 
    { 
     return rhs->equals (this); 
    } 

    virtual bool equals (const C* rhs) const 
    { 
     std::cout << " C==C "; 
     return A::equals (static_cast<const A*> (rhs)) && more_data == rhs->more_data; 
    } 
}; 

bool operator== (const A& lhs, const A& rhs) 
{ 
    return lhs.equals (&rhs); 
} 

int main (int argc, char* argv[]) 
{ 

    A* one = new B; 
    A* two = new B; 
    A* three = new C; 

    std::cout << (*one == *one) << std::endl; 
    std::cout << (*one == *two) << std::endl; 
    std::cout << (*one == *three) << std::endl; 
    std::cout << (*three == *three) << std::endl; 

    return 0; 
} 

它無需dynamic_casts。