2010-01-26 36 views
4

有人可以讓我擺脫這種痛苦嗎?我想弄清楚爲什麼派生的運算符==永遠不會在循環中調用。爲了簡化例子,這裏是我的基類和派生類:派生類中的運算符==永遠不會被調用

class Base { // ... snipped 
    bool operator==(const Base& other) const { return name_ == other.name_; } 
}; 

class Derived : public Base { // ... snipped 
    bool operator==(const Derived& other) const { 
    return (static_cast<const Base&>(*this) == 
      static_cast<const Base&>(other) ? age_ == other.age_ : 
               false); 
}; 

現在,當我實例和比較像這樣...

Derived p1("Sarah", 42); 
Derived p2("Sarah", 42); 
bool z = (p1 == p2); 

...一切都很好。從這裏衍生的==操作符被調用,但是當我遍歷一個列表,在指針的基本對象的列表進行比較的項目...

list<Base*> coll; 

coll.push_back(new Base("fred")); 
coll.push_back(new Derived("sarah", 42)); 
// ... snipped 

// Get two items from the list. 
Base& obj1 = **itr; 
Base& obj2 = **itr2; 

cout << obj1.asString() << " " << ((obj1 == obj2) ? "==" : "!=") << " " 
    << obj2.asString() << endl; 

這裏asString()(這是虛擬的,而不是這裏顯示的簡潔)工作正常,但obj1 == obj2總是調用Baseoperator==即使這兩個對象是Derived

我知道當我發現什麼是錯的時候我會踢自己,但是如果有人可以輕輕地讓我失望,那將是非常感謝。

回答

4

有兩種方法可以解決這個問題。

第一個解決方案。我建議在循環中添加一些額外的類型邏輯,以便知道何時有Base以及何時有Derived。如果你真的只處理Derived對象,請使用

list<Derived*> coll; 

否則把dynamic_cast地方。

第二種解決方案。把相同的邏輯放入你的operator==。首先使其成爲虛擬的,因此左側操作數的類型在運行時確定。然後手動檢查右側操作數的類型。

virtual bool operator==(const Base& other) const { 
    if (! Base::operator==(other)) return false; 
    Derived *other_derived = dynamic_cast< Derived * >(&other); 
    if (! other_derived) return false; 
    return age_ == other_derived->age_; 
} 

但考慮到不同類型的對象很可能是不相等的,可能是你想要的是

virtual bool operator==(const Base& other) const { 
    Derived *other_derived = dynamic_cast< Derived * >(&other); 
    return other_derived 
    && Base::operator==(other) 
    && age_ == other_derived->age_; 
} 
+0

這是有道理的。 「真正的」應用程序使用智能指針進行比較(不幸的是,不是Boost,而是與我正在工作的客戶端的「家庭釀造」),這更加複雜了。不過我會試試這個。值得慶幸的是,雖然這個集合使用了基本的智能指針,但由於上下文的原因,我可以合理確定這些對象都是共同的(派生的)類型。非常感謝。 – 2010-01-27 09:21:49

+0

有趣的是,當我嘗試這個,我發現鏈基類運營商==造成 - 因爲它是虛擬的 - 調用派生運算符==(即無限遞歸)嗯... – 2010-01-27 17:02:19

+0

@Robin:對不起,固定。 – Potatoswatter 2010-01-27 17:19:43

9

這是因爲您沒有讓您的operator ==虛擬,所以在運行時不考慮實際的類型。

不幸的是,只是使運算符==虛擬不會解決您的問題。之所以這樣,是因爲當您通過將參數的類型從基本類型更改爲派生來更改函數簽名時,實際上是在創建一個新函數。這聽起來像你想看看double-dispatch來解決你的問題。

+0

我不相信你實際上可以使運營商的虛擬。 – 2010-01-26 23:17:21

+4

@McWafflesitx - 你的信念是不正確的。 – 2010-01-26 23:18:37

+0

@Samuel:我不知道,謝謝你今天教我一些東西! – LiraNuna 2010-01-26 23:20:39

2

您需要製作operator==virtual並且您需要確保它們兩種方法都具有相同的簽名。即他們可能需要都採取Base。你可以在派生類中有一個能夠處理派生對象的重載operator==

0

對於派生類使用自己的操作符實現,操作符必須在基類中是虛擬的,否則將使用基類實現。

+1

問題是,即使我讓operator ==虛擬化,它也沒有效果,因爲簽名是不同的。我認爲,線索是讓Derived類實現接受一個Base引用並在內部進行投射,因爲在上下文中它應該總是與其他的Derived一起調用。 – 2010-01-27 09:24:08

1

當成員函數是虛擬的時候,虛擬表在運行時用於以指針實際指向的類型(在這種情況下,類Derived)多態地調用該函數。當一個函數不是虛擬的時候,不會進行虛擬表查找,並且調用給定類型的函數(在這種情況下,就是您的類Base)。

這裏,您的operator =()函數不是虛擬的,所以使用指針的類型而不是指針指向的類型。

相關問題