2009-11-07 21 views
10

Nei miei compiti, devo progettare un messaggio di classe; tra gli altri attributi, ha attributo "priorità" (l'obiettivo principale è implementare la coda di priorità).Sovraccarico di operatore <e operatore> nella stessa classe

Come nel contenitore, devo verificare se un oggetto è maggiore di altri, ho sovraccaricato l'operatore '>'. Ora, ho un paio di domande generali su di esso ...

Domanda uno:

Se io sovraccaricare operatore '>', dovrei sovraccaricare operatore '<' per argomenti (Messaggio const &, const Messaggio &)?

La mia opinione è che il sovraccarico sia> e < e di utilizzarlo nel codice genererà un errore:

if(message1 > message2) 
    { ... } 

(Fa il seguente codice chiama operatore> per oggetto Message1, o l'operatore < Message2 oggetto?)

Ma, che cosa se uso operatore come questo:

if(message1 < message2) 
    { ... } 

?

operatore> è dichiarato come amico funzione:

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

Ha bisogno di essere dichiarato come funzione membro?

Grazie.

risposta

21

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

Sì. In realtà, è convenzione nella maggior parte del codice preferire l'uso di < su > (non chiedermi perché, probabilmente storico). Ma più in generale, sovraccaricare sempre la serie completa di operatori correlati; nel tuo caso, probabilmente questo sarebbe anche ==, !=, <= e >=.

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

Chiama sempre ciò che trova nel codice. Per il compilatore C++, non c'è assolutamente alcuna connessione tra > e <. Per noi, sembrano simili ma il compilatore vede due simboli completamente diversi, non correlati. Quindi non c'è ambiguità: il compilatore chiama ciò che vede.

Does it need to be declared as member function?

No. In realtà, è meglio non dichiarato come funzione membro. Dichiararlo come una funzione membro significa che il primo argomento (vale a dire il lato sinistro dell'espressione) deve essere in realtà un oggetto Message, anziché un oggetto che è implicitamente convertibile in un Message.

Per capire questo, si consideri il seguente caso:

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

Ora è possibile scrivere il seguente paragone:

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

ma non può scrivere il seguente:

if (x < y) … 

sebbene esista una conversione implicita da int a RealFraction (utilizzando il primo costruttore).

Se, d'altra parte, si era utilizzata una funzione non membro per implementare l'operatore, entrambi i confronti funzionerebbero perché C++ saprebbe chiamare un costruttore implicito sul primo argomento.

+0

Grazie, ho letto da qualche parte che se sovraccarico un'operazione di una relazione, sembra razionale sovraccaricarli tutti. –

+0

E grazie della risposta di un amico/membro. Capisco cosa intendi :). –

+3

Questa risposta dovrebbe anche indicare che la maggior parte degli operatori relazionali può essere descritta in termini di una manciata di operatori, in genere '<' e '=='.Ad esempio, 'operator> =' può essere scritto: 'bool operator> = (const T & l, const T & r) {return! (L greyfade

8

Sì, si dovrebbe ... ma si può (e forse dovrebbe) mettere in atto tre <, >, <=, >= in termini di l'altra. Questo assicura che si comportino in modo coerente. In genere, < è quello in cui gli altri sono implementati in quanto è l'operatore predefinito utilizzato in set se map s.

E.g. se hai implementato <, potresti definire >, <= e >= in questo modo.

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

== e != sono spesso attuate separatamente. A volte le classi implementano == tale che a == b se e solo se !(a < b) && !(b < a) ma talvolta == viene implementato come rapporto più stretto rispetto a !(a < b) && !(b < a). Fare ciò comporta tuttavia una maggiore complessità per i client della classe.

In alcune situazioni può essere accettabile avere <, >, <= e >= ma non == o !=.

+1

Sì, sì, sì! Ho dimenticato di dirlo ma è importante. Probabilmente dovresti anche dire che il tuo codice pulito sarà * altrettanto efficiente * della codifica manuale di ciascun confronto e indipendentemente. Questo è abbastanza importante perché significa che qualsiasi tentativo di rinunciare a questo consiglio a favore delle prestazioni è un'ottimizzazione prematura. –

+2

Come tale può anche prendere scorciatoie, come 'classe Message: boost :: less_than_comparable ', e 'rel_ops' in' '(quest'ultimo odore in qualche modo sospetto, dato che sono rese utilizzabili da un utilizzo dichiarazione). Con entrambi 'operator <' è sufficiente, e altri vengono implementati in termini di esso. – UncleBens

+0

OTOH, provare a usare 'rel_ops' _can_ è molto doloroso. In genere devi importarli in un altro spazio dei nomi e tendono ad essere troppo avidi. Se la tua classe si trova nello spazio dei nomi globale, puoi finire con 'rel_ops' che fornisce confronti per tutti i tipi di tipi non adatti. –

4

Se l'assegnazione non richiede esplicitamente l'uso dell'overloading dell'operatore, è possibile considerare l'utilizzo di un oggetto funzione. Il motivo è che probabilmente esiste più di un modo per confrontare due messaggi per "minore di" (ad esempio, confrontare i contenuti lessicograficamente, tempo di pubblicazione ecc.) E quindi il significato di operator< non è intuitivamente chiaro.

Con std::priority_queue l'oggetto funzione da utilizzare è specificato come parametro terzo modello (purtroppo avrete anche bisogno di specificare il secondo - sottostante tipo di contenitore):

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

Nell'attuare la propria coda di priorità, è può andare su di esso nel modo seguente:

class MessageQueue 
{ 
    std::vector<Message> messages; 
    ComparePriority compare; 
    //... 
    void push(const Message& msg) 
    { 
     //... 
     if (compare(msg, messages[x])) //msg has lower priority 
     //... 
    } 
}; 
Problemi correlati