2008-11-16 62 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; 
}