Wenn zwei Objekte (des gleichen Typs) verglichen werden, ist es sinnvoll, eine Vergleichsfunktion zu haben, die eine andere Instanz derselben Klasse verwendet. Wenn ich dies als virtuelle Funktion in der Basisklasse implementiere, muss die Signatur der Funktion auch auf die Basisklasse in abgeleiteten Klassen verweisen. Was ist der elegante Weg, dies anzugehen? Sollte der Vergleich nicht virtuell sein?
%Vor%Es hängt von der beabsichtigten Semantik von A, B und C und der Semantik von compare () ab. Der Vergleich ist ein abstrakter Begriff, der nicht unbedingt eine einzige richtige Bedeutung hat (oder überhaupt eine Bedeutung hat). Es gibt keine einzige richtige Antwort auf diese Frage.
Hier sind zwei Szenarien, in denen compare zwei völlig verschiedene Dinge mit derselben Klassenhierarchie bedeutet:
%Vor%Wir können (mindestens) zwei Arten des Vergleichs von zwei Zebras betrachten: Was ist älter und welches hat mehr Volumen? Beide Vergleiche sind gültig und leicht zu berechnen; Der Unterschied besteht darin, dass wir mit einem Volumen einen Zebra mit einem anderen Objekt vergleichen können, aber wir können das Alter nur verwenden, um Zebras mit anderen Tieren zu vergleichen. Wenn compare () die Semantik des Altersvergleichs implementieren soll, macht es keinen Sinn, compare () in der Object-Klasse zu definieren, da die Semantik auf dieser Ebene der Hierarchie nicht definiert ist. Es ist erwähnenswert, dass keines dieser Szenarien irgendein Casting erfordert, wie auch immer, da die Semantik auf der Ebene der Basisklasse definiert ist (ob Objekt beim Vergleichen des Volumens oder Animal beim Vergleich des Alters).
Dies wirft das wichtigere Problem auf - dass einige Klassen nicht für eine einzige catch-all compare () - Funktion geeignet sind. Häufig 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 erfolgen, in dem die Semantik relevant wird, und es sollte trivial sein, sie an untergeordnete Klassen anzupassen (falls überhaupt Anpassungen erforderlich sind). Ein einfacher Vergleich mit compare () oder operator == () ist oft nur bei einfachen Klassen sinnvoll, bei denen die korrekte semantische Implementierung offensichtlich und eindeutig ist.
Lange Geschichte kurz ... "es kommt darauf an".
Ich würde es so umsetzen:
%Vor% Dies ist dem IComparable
-Muster in .NET sehr ähnlich, was sehr gut funktioniert.
BEARBEITEN:
Ein Vorbehalt zu obigem ist, dass a.Compare(b)
(wobei a
ein A und b
B ist) Gleichheit zurückgeben kann und nie eine Ausnahme auslöst, während b.Compare(a)
werden. Manchmal ist das was du willst und manchmal nicht. Wenn dies nicht der Fall ist, möchten Sie wahrscheinlich nicht, dass Ihre Funktion Compare
virtuell ist, oder Sie möchten type_info
s in der Funktion Compare
der Basis vergleichen, wie in:
Beachten Sie, dass die Compare
-Funktionen der abgeleiteten Klassen nicht geändert werden müssen, da sie die Compare
der Basisklasse aufrufen sollten, wo der type_info
-Vergleich auftritt. Sie können jedoch dynamic_cast
in der überschriebenen Funktion Compare
durch static_cast
ersetzen.
Ein Vergleich muss reflektierend sein, also:
%Vor% Also sollte a.equals(b)
false zurückgeben, da B wahrscheinlich Felder enthält, die A nicht hat, was bedeutet, dass b.equals(a)
wahrscheinlich falsch ist.
Daher sollte der Vergleich in C ++ virtuell sein, denke ich, und Sie sollten die Typprüfung verwenden, um zu sehen, dass der Parameter vom "gleichen" Typ ist wie das aktuelle Objekt.
Wenn Sie meinen, dass die Compare () in Klasse B oder C immer ein Objekt der Klasse B oder C übergeben sollte, können Sie, unabhängig von der Signatur, mit Zeigern auf Instanzen anstelle von Instanzen arbeiten und versuchen, einen Downcast durchzuführen Der Zeiger im Code der Methode verwendet etwas wie
%Vor%(Eine solche Überladung wäre nur für diejenigen abgeleiteten Klassen notwendig, die dem Zustand ihrer Eltern etwas hinzufügen, was den Vergleich beeinflusst.)
Ich habe dieses Problem kaum in C ++. Im Gegensatz zu Java müssen wir nicht alle unsere Klassen von derselben Root-Objektklasse erben. Beim Umgang mit vergleichbaren Klassen (/ Wert-Semantiken) ist es sehr unwahrscheinlich, dass sie aus einer polymorphen Hierarchie stammen.
Wenn das Bedürfnis in Ihrer speziellen Situation real ist, sind Sie wieder bei einem Doppel-Dispatch / Multimethods-Problem. Es gibt verschiedene Wege, um es zu lösen (dynamic_cast, Tabellen von Funktionen für die möglichen Interaktionen, Besucher, ...)
Zusätzlich zu dynamic_cast müssen Sie auch eine Referenz oder einen Zeiger übergeben, wahrscheinlich const. Die Compare-Funktion kann wahrscheinlich auch const sein.
%Vor%EDIT: Muss vor dem Hochladen kompiliert werden ...
Ich würde vorschlagen, es nicht virtuell zu machen. Der einzige Nachteil ist, dass Sie explizit sagen müssen, welcher Vergleich verwendet werden soll, wenn die Klassen nicht gleich sind. Aber da Sie müssen, könnten Sie einen Fehler (zur Kompilierzeit) entdecken, der sonst einen Laufzeitfehler verursachen könnte ...
%Vor%