2009-11-07 27 views
10

En mi tarea, tengo que diseñar un mensaje de clase; entre otros atributos, tiene atributo "prioridad" (el objetivo principal es implementar cola de prioridad).Sobrecarga tanto del operador <como del operador> en la misma clase

Como en el contenedor, debo verificar si un objeto es mayor que el otro, he sobrecargado el operador '>'. Ahora, tengo un par de preguntas generales al respecto ...

Pregunta uno:

Si sobrecargo operador '>', en caso de que la sobrecarga del operador < 'de argumentos (Mensaje const &, & mensaje const)?

Mi opinión es que la sobrecarga tanto> y < y su uso en el código generará un error:

if(message1 > message2) 
    { ... } 

(que hace el código siguiente llama operador> para el objeto de mensaje 1, o el operador < Message2 objeto?)

Pero, ¿y si yo uso el operador de esta manera:

if(message1 < message2) 
    { ... } 

?

operador> está declarada como amigo función:

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

¿Tiene que ser declarado como función miembro?

Gracias.

Respuesta

21

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

Sí. De hecho, es una convención en la mayoría de los códigos preferir el uso de < sobre > (no me preguntes por qué, probablemente sea histórico). Pero, en términos más generales, siempre sobrecargue el conjunto completo de operadores relacionados; en su caso, esto probablemente también sea ==, !=, <= y >=.

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

Llama siempre lo que encuentra en el código. Para el compilador C++, no hay absolutamente ninguna conexión entre > y <. Para nosotros, se ven similares, pero el compilador ve dos símbolos completamente diferentes, no relacionados. Entonces no hay ambigüedad: el compilador llama a lo que ve.

Does it need to be declared as member function?

No. De hecho, lo mejor es que no se declare como una función miembro. Declararlo como una función miembro significa que el primer argumento (es decir, el lado izquierdo de la expresión) debe ser realmente un objeto Message, en lugar de un objeto que es implícitamente convertible a Message.

Para entender esto, consideremos el siguiente 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; 
    } 
}; 

Ahora se puede escribir la siguiente comparación:

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

pero no puede escribir lo siguiente:

if (x < y) … 

aunque existe una conversión implícita de int a RealFraction (usando el primer constructor).

Si, por otro lado, hubiera utilizado una función no miembro para implementar el operador, ambas comparaciones funcionarían porque C++ sabría llamar a un constructor implícito en el primer argumento.

+0

Gracias, leí en alguna parte que si sobrecargaba una operación de relación, parece racional sobrecargar todas ellas. –

+0

Y gracias por la respuesta de un amigo/miembro. Veo a que te refieres :). –

+3

Esta respuesta también debe señalar que la mayoría de los operadores relacionales se pueden describir en términos de un puñado de operadores, generalmente '<' y '=='.Por ejemplo, 'operator> =' se puede escribir: 'bool operator> = (const T & l, const T & r) {return! (L greyfade

8

Sí, usted debe ... pero se puede (y probablemente debería) aplicar tres de <, >, <=, >= en términos de la otra. Esto asegura que se comporten de manera consistente. Normalmente, el < es el que los otros se implementan en términos de porque es el operador predeterminado utilizado en set sy map s.

E.g. si implementó <, puede definir >, <= y >= de esta manera.

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

== y != se implementan a menudo por separado. A veces las clases implementan == de modo que a == b si y solo si !(a < b) && !(b < a) pero a veces == se implementa como una relación más estricta que !(a < b) && !(b < a). Sin embargo, hacer esto resulta en una mayor complejidad para el cliente de la clase.

En algunas situaciones puede haber en aceptable tener <, >, <= y >= pero no == o !=.

+1

Sí, sí, sí! Olvidé mencionar esto, pero es importante. También debería decir que su código de limpieza será * tan eficiente * como codificar cada comparación de forma manual e independiente. Esto es bastante importante porque significa que cualquier intento de renunciar a este consejo en favor del rendimiento es una optimización prematura. –

+2

Como tal también se pueden tomar atajos, como 'clase Mensaje: impulso :: menos_como_comparable ', y 'rel_ops' en' '(este último olor algo sospechoso, ya que son utilizables por un uso declaración). Con ambos 'operator <' es suficiente, y otros se implementan en términos de ello. – UncleBens

+0

OTOH, tratando de usar 'rel_ops' _can_ puede ser muy doloroso. Normalmente, tiene que importarlos a otro espacio de nombres y tienden a ser demasiado codiciosos. Si su clase está en el espacio de nombres global, puede terminar con 'rel_ops' proporcionando comparaciones para todo tipo de tipos inadecuados. –

4

Si la asignación no requiere explícitamente el uso de la sobrecarga del operador, también podría considerar el uso de un objeto de función. La razón es que probablemente haya más de una forma de comparar dos mensajes por "menor que" (por ejemplo, comparar los contenidos lexicográficamente, publicar el tiempo, etc.) y, por lo tanto, el significado de operator< no es intuitivamente claro.

Con std::priority_queue se especifica el objeto de función a su uso como el tercer parámetro de plantilla (por desgracia, también debe especificar el segundo - que subyace tipo de contenedor):

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

Al implementar su propia cola de prioridad, puede hacerlo de la siguiente manera:

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