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);
}
4

8 回答 8

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;
}

这与 .NET 中的模式非常相似IComparable,效果很好。

编辑:

对上述内容的一个警告是a.Compare(b)(where ais a and bis a B) 可能会返回相等,并且永远不会抛出异常,而b.Compare(a)会。有时这是你想要的,有时不是。如果不是,那么您可能不希望您的Compare函数是虚拟的,或者您希望比较type_infoCompare函数中的 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函数不需要更改,因为它们应该调用基类的Comparetype_info比较将在其中进行。但是,您可以dynamic_cast将覆盖Compare函数中的 替换为static_cast

于 2008-11-16T17:46:50.760 回答
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 */
    }
  }
};
于 2008-11-16T18:11:12.617 回答
1

它取决于 A、B 和 C 的预期语义以及 compare() 的语义。比较是一个抽象概念,不一定有一个正确的含义(或任何含义,就此而言)。这个问题没有单一的正确答案。

这是两个场景,其中比较意味着具有相同类层次结构的两个完全不同的事物:

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() 没有任何意义,因为语义不是在层次结构的这个级别定义的。值得注意的是,这些场景都不需要任何强制转换,因为语义是在基类级别定义的(比较体积时是 Object,比较年龄时是 Animal)。

这引发了一个更重要的问题——某些类不适合单个包罗万象的 compare() 函数。通常,实现多个明确说明正在比较的内容的函数更有意义,例如 compare_age() 和 compare_volume()。这些函数的定义可以发生在继承层次结构中语义变得相关的点,并且将它们调整到子类应该是微不足道的(如果需要调整的话)。使用 compare() 或 operator==() 的简单比较通常只对正确语义实现明显且明确的简单类有意义。

长话短说……“这取决于”。

于 2009-05-08T17:43:06.200 回答
0

如果您的意思是 B 或 C 类中的 Compare() 应始终传递 B 或 C 类的对象,无论签名说什么,您都可以使用指向实例而不是实例的指针,并尝试将指针向下转换该方法的代码使用类似的东西

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

(这种重载仅对那些在其父状态中添加影响比较的东西的派生类是必要的。)

于 2008-11-16T15:53:48.120 回答
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)应该返回假,因为 B 可能包含 A 没有的字段,这意味着b.equals(a)可能是假的。

因此,在 C++ 中,我猜比较应该是虚拟的,并且您应该使用类型检查来查看参数与当前对象的类型“相同”。

于 2008-11-16T16:06:23.880 回答
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;
    }
}

编辑:发布前必须编译...

于 2008-11-16T16:09:55.170 回答
0

我在 C++ 中几乎没有这个问题。与 Java 不同,我们不需要从同一个根 Object 类继承所有类。在处理可比较(/值语义)类时,它们不太可能来自多态层次结构。

如果在您的特定情况下确实需要,那么您又回到了双重调度/多方法问题。有多种解决方法(dynamic_cast、可能交互的函数表、访问者……)

于 2008-11-16T16:43:31.047 回答
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;
}
于 2008-11-16T19:20:02.010 回答