Frage

Wenn zwei Objekte zu vergleichen (des gleichen Typs), macht es Sinn, eine Vergleichsfunktion zu haben, die eine andere Instanz derselben Klasse nimmt. Wenn ich dies als eine virtuelle Funktion in der Basisklasse implementieren, dann hat die Signatur der Funktion auch die Basisklasse in abgeleiteten Klassen zu verweisen. Was ist die elegante Möglichkeit, dies zu bewältigen? Sollte der Vergleich nicht virtuell sein?

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);
}
War es hilfreich?

Lösung

Es hängt von der beabsichtigten Semantik von A, B und C und der Semantik vergleichen (). Der Vergleich ist ein abstraktes Konzept, das nicht notwendigerweise eine einzige richtige Bedeutung hat (oder irgendeine Bedeutung überhaupt, was das betrifft). Es gibt keine einzige richtige Antwort auf diese Frage.

Hier zwei Szenarien, in denen zu vergleichen, bedeuten, dass zwei völlig verschiedene Dinge mit der gleichen Klassenhierarchie:

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

Wir betrachten (mindestens) zwei Möglichkeiten, den Vergleich von zwei Zebras: die älter ist, und welche mehr Volumen hat? Beide Vergleiche gültig sind und leicht berechenbar; der Unterschied ist, dass wir Volumen ein Zebra mit einem anderen Objekt vergleichen können, aber wir können nur das Alter verwenden Zebras mit anderen Tieren zu vergleichen. Wenn wir vergleichen wollen (), um die Alter Vergleich Semantik zu implementieren, macht es keinen Sinn () in der Objektklasse zu definieren, zu vergleichen, da die Semantik nicht auf dieser Ebene der Hierarchie definiert. Es ist erwähnenswert, dass keines dieser Szenarien Giessharzsystemen erfordern, was auch immer, da die Semantik auf der Ebene der Basisklasse definiert werden (ob es Gegenstand sein, wenn Volumen zu vergleichen, oder Tier, wenn Alter zu vergleichen).

Dies wirft die wichtigere Frage -, dass einige Klassen nicht auf einen einzigen Catch-All geeignet sind, vergleichen () Funktion. Oft ist es sinnvoller mehrere Funktionen zu implementieren, die explizit angeben, was verglichen wird, wie compare_age () und compare_volume (). Die Definition dieser Funktionen kann an dem Punkt in der Vererbungshierarchie auftreten, wo die Semantik relevant werden, und es sollte trivial sein, sie Klassen Kind anzupassen (wenn die Notwendigkeit überhaupt Anpassung). Einfacher Vergleich Vergleichen () oder Operator == () oft nur Sinn, mit einfachen Klassen machen, wo die richtige semantische Implementierung ist offensichtlich und eindeutig ist.

Lange Rede kurzer Sinn ... "es kommt".

Andere Tipps

Ich würde es so implementieren:

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

Dies ist sehr ähnlich das IComparable Muster in .NET, die sehr gut funktioniert.

EDIT:

Eine Einschränkung auf die obige ist, dass a.Compare(b) (wo a ein A ist und b ist ein B) Gleichheit zurückkehren kann, und nie eine Ausnahme auslösen, während b.Compare(a) Willen. Manchmal ist es das, was Sie wollen, und manchmal ist es nicht. Wenn es nicht ist, dann haben Sie wahrscheinlich nicht wollen, Ihre Compare Funktion virtuell sein, oder wollen Sie type_infos in der Basis Compare Funktion, wie in vergleichen:

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

Beachten Sie, dass abgeleitete Klassen Compare Funktionen müssen nicht geändert werden, da sie die Basisklasse Compare nennen sollte, wo der type_info Vergleich auftreten. Sie können jedoch, ersetzen Sie die dynamic_cast in der überschriebenen Compare Funktion mit einem static_cast.

Wahrscheinlich, ich es so tun würde:

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

Ein Vergleich muss reflektierend sein, so:

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

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

So sollte die a.equals(b) false zurück, da B wahrscheinlich enthält Felder, die A nicht über das Mittel b.equals(a) wahrscheinlich falsch sein wird.

So wird in C ++ sollte der Vergleich virtuell sein glaube ich, und Sie sollen typen zu überprüfen, verwenden, dass der Parameter der „gleichen“ Art, wie das aktuelle Objekt ist.

Wenn Sie meinen, dass das Vergleichen () der Klasse B oder C sollte immer ein Objekt der Klasse B oder C, ganz gleich weitergegeben werden, was die Unterschrift sagt, können Sie mit Zeigern auf Instanzen anstelle von Instanzen arbeiten und versuchen, niedergeschlagenen der Zeiger in den Code der Methode mit so etwas wie

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

(Eine solche Überlastung würde nur für die abgeleiteten Klassen erforderlich sein, die auf den Zustand ihrer Eltern etwas hinzufügen, die den Vergleich beeinflusst.)

Ich habe kaum dieses Problem in C ++. Im Gegensatz zu Java sind wir nicht von derselben Wurzel Objektklasse erben alle Klassen erforderlich. Wenn mit vergleichbaren (/ Wert Semantik) Klassen zu tun, ist es sehr unwahrscheinlich ist, sie von einer polymorphen Hierarchie kommen haben.

Wenn die Notwendigkeit in Ihrer speziellen Situation wirklich ist, sind Sie wieder in eine Doppel Versand / Multimethoden Problem. Es gibt verschiedene Möglichkeiten, es zu lösen (dynamic_cast, Tabellen von Funktionen für die möglichen Wechselwirkungen, Besucher, ...)

Neben dem dynamic_cast, müssen Sie auch einen Verweis oder einen Zeiger passieren, wahrscheinlich konst. Die Compare-Funktion kann wahrscheinlich auch const sein.

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

EDIT: Muss kompiliert vor der Veröffentlichung ...

Ich würde vorschlagen, es nicht virtuell zu machen. Der einzige Nachteil ist, dass man explizit sagen, welche verwenden vergleichen, wenn die Klassen nicht gleich sind. Aber weil Sie müssen, können Sie einen Fehler entdecken (zum Zeitpunkt der Kompilierung), die sonst könnte einen Laufzeitfehler verursachen ...

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;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top