2009-11-07 7 views
10

In meinen Hausaufgaben muss ich eine Klasse Nachricht entwerfen; neben anderen Attributen hat es Attribut "Priorität" (Hauptziel ist die Implementierung der Prioritätswarteschlange).Überladen von Operator <und Operator> in derselben Klasse

Wie im Container muss ich überprüfen, ob ein Objekt größer als andere ist, ich habe den Operator '>' überladen. Nun, ich habe ein paar allgemeinen Fragen zu diesem Thema ...

Frage eins:

Wenn ich überlastete Operator '>', soll ich überlastete Operator '<' für Argumente (const Nachricht &, const Nachricht &)

Meine Meinung ist, dass sowohl eine Überlastung> und < und es im Code verwendet, wird ein Fehler generiert:

if(message1 > message2) 
    { ... } 

(Ist der folgende Code ruft Operator> für message1 Objekt oder Betreiber < Message2 Objekt?)

Aber was, wenn ich Operator wie folgt verwenden:

if(message1 < message2) 
    { ... } 

?

Operator> als Freund Funktion deklariert:

friend bool operator>(const Message& m1, const Message& m2) 

es als Mitglied Funktion deklariert werden muss?

Vielen Dank.

Antwort

21

If I overload operator '>', should I overload operator '<' for argumenst (const Message&, const Message&)?

Ja. In der Tat ist es Konvention in den meisten Code, die Verwendung von < über > bevorzugen (fragen Sie mich nicht, warum, wahrscheinlich historische). Aber im Allgemeinen, überladen Sie immer den kompletten Satz von verwandten Operatoren; in Ihrem Fall wäre dies wahrscheinlich auch ==, !=, <= und >=.

(Does the following code calls operator > for message1 object, or operator < message2 object?)

Es ruft immer an, was es im Code findet. Für den C++ - Compiler gibt es absolut keine Verbindung zwischen > und <. Für uns sehen sie ähnlich aus, aber der Compiler sieht zwei völlig unterschiedliche, nicht verwandte Symbole. Es gibt also keine Zweideutigkeit: Der Compiler nennt was er sieht.

Does it need to be declared as member function?

Nein. In der Tat ist es am besten nicht als Mitglied Funktion deklariert. Das Deklarieren als eine Mitgliedsfunktion bedeutet, dass das erste Argument (d. H. Die linke Seite des Ausdrucks) wirklich ein Message-Objekt sein muss und nicht ein Objekt, das implizit in eine Message konvertierbar ist.

Um dies zu verstehen, betrachten wir den folgenden Fall:

struct RealFraction { 
    RealFraction(int x) { this.num = x; this.den = 1; } 
    RealFraction(int num, int den) { normalize(num, den); } 
    // Rest of code omitted. 

    bool operator <(RealFraction const& rhs) { 
     return num * rhs.den < den * rhs.num; 
    } 
}; 

Jetzt können Sie den folgenden Vergleich schreiben:

int x = 1; 
RealFraction y = 2; 
if (y < x) … 

aber Sie kann die folgende nicht schreiben:

if (x < y) … 

obwohl es eine implizite Konvertierung von 01 gibtzu RealFraction (mit dem ersten Konstruktor).

Wenn Sie andererseits eine Nichtmitgliedsfunktion zur Implementierung des Operators verwendet hätten, würden beide Vergleiche funktionieren, weil C++ einen impliziten Konstruktor für das erste Argument aufrufen könnte.

+0

Danke, ich habe irgendwo gelesen, dass, wenn ich eine Relation Operation überladen, es rational scheint, alle von ihnen zu überlasten. –

+0

Und danke über Freund/Mitglied antworten. Ich verstehe was du meinst :). –

+3

Diese Antwort sollte auch darauf hinweisen, dass die meisten relationalen Operatoren in Form einer Handvoll Operatoren beschrieben werden können, normalerweise '<' und '=='.Zum Beispiel kann 'operator> =' geschrieben werden: 'bool operator> = (const T & l, const T & r) {return! (L greyfade

8

Ja, Sie sollten ... aber man kann (und wohl auch sollte) implementieren drei <, >, <=, >= in Bezug auf die anderen. Dies stellt sicher, dass sie sich konsistent verhalten. In der Regel ist < derjenige, bei dem die anderen implementiert sind, da es der Standardoperator ist, der in set s und map s verwendet wird.

z. Wenn Sie < implementiert haben, können Sie >, <= und >= so definieren.

inline bool operator>(const Message& lhs, const Message& rhs) 
{ 
    return rhs < lhs; 
} 

inline bool operator<=(const Message& lhs, const Message& rhs) 
{ 
    return !(rhs < lhs); 
} 

inline bool operator>=(const Message& lhs, const Message& rhs) 
{ 
    return !(lhs < rhs); 
} 

== und != werden oft separat umgesetzt. Manchmal implementieren Klassen == so, dass a == b wenn und nur wenn !(a < b) && !(b < a) aber manchmal == als eine strengere Beziehung als !(a < b) && !(b < a) implementiert ist. Dies führt jedoch zu mehr Komplexität für den Client der Klasse.

In einigen Situationen in kann akzeptabel sein <, >, <= und >= zu haben, aber nicht == oder !=.

+1

Ja, ja, ja! Ich habe vergessen, dies zu erwähnen, aber es ist wichtig. Sie sollten wahrscheinlich auch sagen, dass Ihr sauberer Code * genauso effizient ist * wie jeder Vergleich manuell und unabhängig codiert. Dies ist sehr wichtig, weil es bedeutet, dass jeder Versuch, auf diese Empfehlung zugunsten der Leistung zu verzichten, eine vorzeitige Optimierung ist. –

+2

Als solche kann man auch Abkürzungen nehmen, wie 'class Message: boost :: less_than_comparable ' und 'rel_ops' in' '(letztere riechen etwas verdächtig, da sie durch eine Verwendung nutzbar gemacht werden Erklärung). Mit beiden ist "operator <" genug, und andere werden in Bezug darauf implementiert. – UncleBens

+0

OTOH, versuchen, 'rel_ops' _can_ sehr schmerzhaft zu verwenden. Normalerweise müssen Sie sie in einen anderen Namensraum importieren und sie sind viel zu gierig. Wenn sich Ihre Klasse im globalen Namespace befindet, können Sie mit "rel_ops" einen Vergleich für alle Arten ungeeigneter Typen erstellen. –

4

Wenn die Zuweisung nicht explizit die Verwendung von Operatorüberladung erfordert, können Sie auch ein Funktionsobjekt in Betracht ziehen. Der Grund ist, dass es wahrscheinlich mehr als eine Möglichkeit gibt, zwei Nachrichten für "weniger als" zu vergleichen (z. B. Inhalte lexikografisch, Buchungszeit usw.), und daher ist die Bedeutung von operator< nicht intuitiv klar.

Mit std::priority_queue der Funktion Objekt Verwendung als dritten Template-Parameter angegeben wird (leider werden Sie auch den zweiten angeben müssen - darunter liegenden Containertyp):

#include <queue> 
#include <string> 
#include <functional> 
#include <vector> 

class Message 
{ 
    int priority; 
    std::string contents; 
    //... 
public: 
    Message(int priority, const std::string msg): 
     priority(priority), 
     contents(msg) 
    {} 
    int get_priority() const { return priority; } 
    //... 
}; 

struct ComparePriority: 
    std::binary_function<Message, Message, bool> //this is just to be nice 
{ 
    bool operator()(const Message& a, const Message& b) const 
    { 
     return a.get_priority() < b.get_priority(); 
    } 
}; 

int main() 
{ 
    typedef std::priority_queue<Message, std::vector<Message>, ComparePriority> MessageQueue; 
    MessageQueue my_messages; 
    my_messages.push(Message(10, "Come at once")); 
} 

Wenn Sie Ihre eigene Prioritätswarteschlange Implementierung Sie kann auf folgende Weise gehen: