2009-11-07 19 views
10

W mojej pracy domowej muszę zaprojektować wiadomość klasy; wśród innych atrybutów ma atrybut "priorytet" (głównym celem jest zaimplementowanie kolejki priorytetów).Przeciążanie operatora <i operatora> w tej samej klasie

Podobnie jak w kontenerze muszę sprawdzić, czy jeden obiekt jest większy od drugiego, czy mam przeciążony operator ">". Teraz mam kilka ogólnych pytań o to ...

Pytanie pierwsze:

Gdybym przeciążać operator '>', powinien przeciążać operatora '<' dla argumentów (const Wiadomość &, const Wiadomość &)?

Moja opinia jest taka, że ​​przeciążenia zarówno> i < i używanie go w kodzie wygeneruje błąd:

if(message1 > message2) 
    { ... } 

(czy następujący kod wywołuje operator> dla obiektu message1 lub operator < komunikat2 obiekt?)

Ale co, jeśli mogę użyć operatora tak:

if(message1 < message2) 
    { ... } 

?

operator> jest zadeklarowana jako znajomego funkcję:

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

Czy to muszą być deklarowane jako funkcja członkiem?

Dziękuję.

Odpowiedz

21

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

Tak. W rzeczywistości jest to konwencja w większości kodów, aby preferować użycie < przez > (nie pytaj mnie dlaczego, prawdopodobnie historyczne). Ale bardziej ogólnie, zawsze przeciążaj cały zestaw powiązanych operatorów; w twoim przypadku prawdopodobnie będzie to także ==, !=, i >=.

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

Zawsze wywołuje to, co znajdzie w kodzie. W przypadku kompilatora C++ absolutnie nie ma połączenia między > i <. Dla nas wyglądają podobnie, ale kompilator widzi dwa zupełnie różne, niepowiązane symbole. Nie ma więc dwuznaczności: kompilator wywołuje to, co widzi.

Does it need to be declared as member function?

Nie. W rzeczywistości najlepiej nie być zadeklarowanym jako funkcja członka. Zgłaszanie go jako funkcji składowej oznacza, że ​​pierwszy argument (tj. Lewa strona wyrażenia) musi być rzeczywiście obiektem Message, a nie obiektem, który jest domyślnie wymienialny na Message.

Aby to zrozumieć, należy rozważyć następujące sprawy:

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

Teraz można napisać następujące porównania:

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

ale nie może napisać następujący:

if (x < y) … 

mimo że istnieje niejawna konwersja od int do RealFraction (przy użyciu pierwszego konstruktora).

Jeśli, z drugiej strony, użyłeś funkcji nie-członka do implementacji operatora, oba porównania będą działały, ponieważ C++ będzie wiedział o wywołaniu niejawnego konstruktora na pierwszym argumencie.

+0

Dziękuję, przeczytałem gdzieś, że jeśli przeciążę jedną operację relacji, wydaje mi się racjonalne przeładowanie wszystkich z nich. –

+0

I dziękuję ci za odpowiedź przyjaciela/członka. Rozumiem, co masz na myśli :). –

+3

Ta odpowiedź powinna również wskazywać, że większość operatorów relacyjnych można opisać za pomocą kilku operatorów, zwykle '<' i '=='.Na przykład można wpisać 'operator> =': operator bool> = (const T & l, const T & r) {return! (L greyfade

8

Tak, powinien ... ale można (i zapewne należy) realizuje trzy <, >, <=, >= pod względem drugiej. Zapewnia to, że zachowują się konsekwentnie. Zazwyczaj < to ta, której pozostałe elementy są implementowane, ponieważ jest to domyślny operator używany w set s i map s.

E.g. jeśli zaimplementowano <, można zdefiniować >, <= i >= w ten sposób.

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

== i != są często realizowane oddzielnie. Czasami klasy implementują == tak, że a == b wtedy i tylko wtedy, gdy !(a < b) && !(b < a), ale czasami == jest zaimplementowane jako ściślejsza relacja niż !(a < b) && !(b < a). Wykonanie tego powoduje jednak większą złożoność dla klienta klasy.

W niektórych sytuacjach może być do przyjęcia, <, >, <= i >= ale nie == lub !=.

+1

Tak, tak, tak! Zapomniałem o tym wspomnieć, ale jest to ważne. Powinieneś prawdopodobnie powiedzieć, że twój czysty kod będzie * równie efektywny * jak kodowanie każdego porównania ręcznie i niezależnie. Jest to dość ważne, ponieważ oznacza to, że każda próba rezygnacji z tej porady na rzecz wydajności jest przedwczesną optymalizacją. –

+2

Jako takie można również stosować skróty, takie jak 'class Message: boost :: less_than_comparable ' i 'rel_ops' w' '(ten drugi zapach jest nieco podejrzany, ponieważ są one przydatne do użycia przez użycie deklaracja). Zarówno 'operator <' jest wystarczający, a inni są zaimplementowani pod względem tego. – UncleBens

+0

OTOH, próba użycia 'rel_ops' _can_ może być bardzo bolesna. Zazwyczaj musisz zaimportować je do innej przestrzeni nazw i są one zdecydowanie zbyt chciwe. Jeśli twoja klasa znajduje się w globalnej przestrzeni nazw, możesz skończyć słowem 'rel_ops', zapewniając porównania dla wszystkich rodzajów nieodpowiednich typów. –

4

Jeśli przypisanie nie wymaga jawnie użycia przeciążenia operatora, można również rozważyć użycie obiektu funkcji. Powodem jest to, że prawdopodobnie istnieje więcej niż jeden sposób porównywania dwóch komunikatów dla "mniej niż" (np. Porównywanie treści leksykograficznie, publikowanie czasu itp.), A zatem znaczenie operator< nie jest intuicyjnie jasne.

Z std::priority_queue przedmiotem funkcja do użycia jest określony jako trzeci parametr szablonu (niestety musisz również podać drugą - bazowego typu pojemnika):

#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")); 
} 

Przy wdrażaniu własną kolejkę priorytetową, ty można go wykonać w następujący sposób:

Powiązane problemy