2008-11-16 87 views
9

比較兩個對象(同一類型)時,有一個比較函數需要另一個同一類的實例。如果我將它作爲基類中的虛函數實現,那麼函數的簽名也必須在派生類中引用基類。解決這個問題的優雅方法是什麼?比較不應該是虛擬的?優雅對象比較

class A 
{ 
    A(); 
    ~A(); 
    virtual int Compare(A Other); 
} 

class B: A 
{ 
    B(); 
    ~B(); 
    int Compare(A Other); 
} 

class C: A 
{ 
    C(); 
    ~C(); 
    int Compare(A Other); 
} 
+2

不,使用==運算符是有意義的。這就是它的目的。不需要比較功能。 – jalf 2008-11-16 17:34:18

+0

@jalf我認爲他希望做比strcmp()更大,更小,更小的比較。注意Compare()返回一個int值,而不是一個bool值。 – 2009-05-08 20:48:31

回答

1

這取決於A,B和C的預期語義和語義比較()。比較是一個抽象的概念,不一定具有單一的正確含義(或者就此而言任何意義)。這個問題沒有單一的正確答案。

這裏有兩種情況,其中比較是指具有相同的類層次兩個完全不同的事情:

class Object 
{ 
    virtual int compare(const Object&) = 0; 
    float volume; 
}; 

class Animal : Object 
{ 
    virtual int compare(const Object&); 
    float age; 
}; 

class Zebra : Animal 
{ 
    int compare(const Object&); 
}; 

我們可以(至少)考慮比較兩個斑馬的兩種方法:這是舊的,並且具有更大的體積?兩種比較都是有效的並且易於計算;區別在於我們可以使用音量來比較斑馬與其他任何物體,但我們只能用年齡來比較斑馬與其他動物。如果我們想要compare()來實現年齡比較語義,那麼在Object類中定義compare()是沒有意義的,因爲語義沒有在層次結構的這個層次上定義。值得注意的是,這些場景都不需要任何投射,因爲語義是在基類層次上定義的(無論是在比較音量時是對象還是在比較年齡時是動物)。

這引發了一個更重要的問題 - 某些類不適合單個catch-all compare()函數。通常情況下,實現多個明確聲明正在比較的函數(比如compare_age()和compare_volume())會更有意義。這些函數的定義可以發生在語義變得相關的繼承層次結構中,並且將它們適配到子類(如果需要適應),應該是微不足道的。使用compare()或operator ==()的簡單比較通常只對簡單的類有意義,其中正確的語義實現是顯而易見且明確的。

長話短說......「這取決於」。

0

如果你的意思是,在B類或C比較()應該總是通過B類或C的對象,不管簽名說什麼,你可以使用指針工作,以實例來代替的情況下,並嘗試向下轉換的方法的代碼使用的東西的指針一樣

int B::Compare(A *ptr) 
{ 
    other = dynamic_cast <B*> (ptr); 
    if(other) 
     ... // Ok, it was a pointer to B 
} 

(這樣的重載只爲增加他們的父母的東西,影響比較國家的派生類是必要的。)

0

一個比較必須是反射的,所以:

let a = new A 
let b = new B (inherits from A) 

if (a.equals(b)) 
then b.equals(a) must be true! 

所以a.equals(b)應該返回false,由於B可能包含一個沒有,這意味着b.equals(a)可能會是假的領域。

因此,在C++中,比較應該是虛擬的,我們應該使用類型檢查來查看參數與當前對象的「相同」類型。

+0

比較,不等於。認爲strcmp()。 – 2009-05-08 21:03:07

0

除了dynamic_cast之外,還需要傳遞一個引用或指針,可能是const。比較函數也可能是const。

class B: public A 
{ 
    B(); 
    virtual ~B(); 
    virtual int Compare(const A &Other) const; 
}; 


int B::Compare(const A &Other) const 
{ 
    const B *other = dynamic_cast <const B*> (&Other); 
    if(other) { 
     // compare 
    } 
    else { 
     return 0; 
    } 
} 

編輯:必須在發佈前編譯...

+0

警告:在B :: Compare中,Other是一個對象,因此您的代碼會嘗試將對象轉換爲指針。而且,返回零意味着平等;我會提出一些例外 – 2008-11-16 16:19:16

+0

你會在這種情況下引發一些例外?那是糟糕的。 – coppro 2008-11-16 18:09:19

0

我幾乎沒有這個問題在C++中。與Java不同,我們不需要從同一個根對象類繼承我們的所有類。在處理類似的(/值語義)類時,它們不太可能來自多態層次結構。

如果在特定情況下需求是真實的,那麼您又回到了雙派/多方法問題。有多種方法來解決這個問題(dynamic_cast的,對於可能存在的相互作用的功能表,遊客,...)

1

我會實現它是這樣的:

class A{ 
    int a; 

public: 
    virtual int Compare(A *other); 
}; 


class B : A{ 
    int b; 

public: 
    /*override*/ int Compare(A *other); 
}; 

int A::Compare(A *other){ 
    if(!other) 
     return 1; /* let's just say that non-null > null */ 

    if(a > other->a) 
     return 1; 

    if(a < other->a) 
     return -1; 

    return 0; 
} 

int B::Compare(A *other){ 
    int cmp = A::Compare(other); 
    if(cmp) 
     return cmp; 

    B *b_other = dynamic_cast<B*>(other); 
    if(!b_other) 
     throw "Must be a B object"; 

    if(b > b_other->b) 
     return 1; 

    if(b < b_other->b) 
     return -1; 

    return 0; 
} 

這是非常相似的IComparable模式在.NET中,它工作得很好。

編輯:

一個警告上述是a.Compare(b)(其中a是A和b是B)可以返回平等,並且將從未拋出異常,而b.Compare(a)意願。有時候這就是你想要的,有時候不是。如果不是,那麼你可能不希望您的Compare功能是虛擬的,或者你想在基礎Compare功能比較type_info S,如:

int A::Compare(A *other){ 
    if(!other) 
     return 1; /* let's just say that non-null > null */ 

    if(typeid(this) != typeid(other)) 
     throw "Must be the same type"; 

    if(a > other->a) 
     return 1; 

    if(a < other->a) 
     return -1; 

    return 0; 
} 

注意,派生類Compare功能別因爲他們應該調用基類的Compare,其中type_info將發生比較。但是,您可以使用static_cast替代覆蓋的Compare函數中的dynamic_cast

1

也許,我會做這樣的:

class A 
{ 
public: 
    virtual int Compare (const A& rhs) const 
    { 
    // do some comparisons 
    } 
}; 

class B 
{ 
public: 
    virtual int Compare (const A& rhs) const 
    { 
    try 
    { 
     B& b = dynamic_cast<A&>(rhs) 
     if (A::Compare(b) == /* equal */) 
     { 
     // do some comparisons 
     } 
     else 
     return /* not equal */; 
    } 
    catch (std::bad_cast&) 
    { 
     return /* non-equal */ 
    } 
    } 
}; 
0

我會建議不要讓虛擬的。唯一的缺點是,如果類不相同,則顯式必須說明使用哪種比較。但是,因爲你必須這樣做,你可能會看到一個錯誤(在編譯時),否則可能會導致運行時錯誤...

class A 
{ 
    public: 
    A(){}; 
    int Compare(A const & Other) {cout << "A::Compare()" << endl; return 0;}; 
}; 

class B: public A 
{ 
    public: 
    B(){}; 
    int Compare(B const & Other) {cout << "B::Compare()" << endl; return 0;}; 
}; 

class C: public A 
{ 
    public: 
    C(){}; 
    int Compare(C const & Other) {cout << "C::Compare()" << endl; return 0;}; 
}; 

int main(int argc, char* argv[]) 
{ 
    A a1; 
    B b1, b2; 
    C c1; 

    a1.Compare(b1);  // A::Compare() 
    b1.A::Compare(a1); // A::Compare() 
    b1.Compare(b2);  // B::Compare() 
    c1.A::Compare(b1); // A::Compare() 

    return 0; 
}