La respuesta aceptada por Michael Burr es bastante bueno en la explicación de la técnica, pero a partir de los comentarios parece que además del "cómo" estás interesado en el "por qué". Las razones principales para proporcionar sobrecargas al operador para un tipo dado son mejorar la legibilidad y proporcionar una interfaz requerida.
Si usted tiene un tipo para el que no hay una sola comúnmente se entiende que significa para un operador en el dominio de su problema, a continuación, siempre que como una sobrecarga del operador hace que el código sea más legible:
std::complex<double> a(1,2), b(3,4), c(5, 6);
std::complex<double> d = a + b + c; // compare to d = a.add(b).add(c);
std::complex<double> e = (a + d) + (b + c); // e = a.add(d).add(b.add(c));
Si el tipo de tiene una propiedad dada que se expresará naturalmente con un operador, puede sobrecargar ese operador en particular para su tipo. Considere por ejemplo, que desea comparar sus objetos para la igualdad. Brindar operator==
(y operator!=
) puede darle una forma sencilla de hacerlo.Esto tiene la ventaja de cumplir con una interfaz común que se puede utilizar con algoritmos que dependen de la igualdad:
struct type {
type(int x) : value(x) {}
int value;
};
bool operator==(type const & lhs, type const & rhs)
{ return lhs.value == rhs.value; }
bool operator!=(type const & lhs, type const & rhs)
{ return !lhs == rhs; }
std::vector<type> getObjects(); // creates and fills a vector
int main() {
std::vector<type> objects = getObjects();
type t(5);
std::find(objects.begin(), objects.end(), t);
}
Tenga en cuenta que cuando se implementa el algoritmo find
, que depende de ==
se está definiendo. La implementación de find funcionará tanto con tipos primitivos como con cualquier tipo definido por el usuario que tenga un operador de igualdad definido. Hay una interfaz única común que tiene sentido. Compare eso con la versión de Java, donde la comparación de tipos de objetos debe realizarse a través de la función de miembro .equals
, mientras que la comparación de tipos primitivos se puede hacer con ==
. Al permitirle sobrecargar a los operadores, puede trabajar con tipos definidos por el usuario de la misma manera que con los tipos primitivos.
Lo mismo ocurre con los pedidos. Si hay un orden bien definido (parcial) en el dominio de su clase, proporcionar operator<
es una forma sencilla de implementar ese orden. Código será legible, y su tipo será utilizable en todas las situaciones donde se requiere un orden parcial, como el interior de contenedores asociativos:
bool operator<(type const & lhs, type const & rhs)
{
return lhs < rhs;
}
std::map<type, int> m; // m will use the natural `operator<` order
Un error común cuando la sobrecarga de operadores se introdujo en el lenguaje es el de la 'golden hammer 'Una vez que tienes un martillo dorado, todo parece un clavo, y se ha abusado de la sobrecarga del operador.
Es importante tener en cuenta que la razón de la sobrecarga en primer lugar es mejorar la legibilidad. La legibilidad solo se mejora si cuando un programador mira el código, las intenciones de cada operación son claras a primera vista, sin tener que leer las definiciones. Cuando vea que se están agregando dos números complejos como a + b
, sabe lo que hace el código. Si la definición del operador no es natural (decide implementarla como si solo agregara la parte real), entonces el código será más difícil de leer que si hubiera proporcionado una función (miembro). Si el significado de la operación no está bien definido para su tipo sucede lo mismo:
MyVector a, b;
MyVector c = a + b;
¿Cuál es c
? Es un vector donde cada elemento i
es la suma de los elementos respectivos de a
y b
, o es un vector creado al concatenar los elementos de a
antes de los elementos de b
. Para entender el código, lo que tendría que ir a la definición de la operación, y eso significa que la sobrecarga del operador es menos legible que proporciona una función:
MyVector c = append(a, b);
El conjunto de operadores que pueden ser sobrecargado no se limita a los operadores aritméticos y relacionales. Puede sobrecargar operator[]
para indexar en un tipo o operator()
para crear un exigible objeto que se puede utilizar como una función (éstos se llaman funtores) o que simplificará el uso de la clase:
class vector {
public:
int operator[](int);
};
vector v;
std::cout << v[0] << std::endl;
class matrix {
public:
int operator()(int row, int column);
// operator[] cannot be overloaded with more than 1 argument
};
matrix m;
std::cout << m(3,4) << std::endl;
Hay otros usos de la sobrecarga del operador. En particular, el operator,
puede sobrecargarse de una manera realmente sofisticada con fines de metaprogramación, pero eso es probablemente mucho más complejo de lo que realmente le importa ahora.
+1 para los operadores que no puede sobrecargar. – FrustratedWithFormsDesigner
Gracias por la asistencia ... lamentablemente, aunque entiendo mejor a los operadores sobrecargados, mi comprensión sobre cómo usarlos todavía no está lo suficientemente cerca como para querer usarlos. Pero al menos ahora entiendo mejor, ¡gracias de nuevo! – Jeff
+1 Fantástica respuesta. En realidad, nunca se me ocurrió que el uso de '<<' and '>>' para la transmisión se debía en realidad a la sobrecarga del operador en la biblioteca estándar. Siempre pensé que era solo una de esas peculiaridades de C++ que estos operadores solo querían decir cosas diferentes según el contexto. – LeopardSkinPillBoxHat