En C++, podemos hacer que los operadores funcionen para clases definidas por el usuario. Esto significa que C++ tiene la capacidad de proporcionar a los operadores un significado especial para un tipo de datos, esta capacidad se conoce como sobrecarga de operadores. Por ejemplo, podemos sobrecargar un operador ‘+’ en una clase como String para que podamos concatenar dos strings simplemente usando +. Otras clases de ejemplo donde los operadores aritméticos pueden estar sobrecargados son Números complejos, Números fraccionarios, Entero grande, etc.
La sobrecarga de operadores es un polimorfismo en tiempo de compilación. Es una idea de dar un significado especial a un operador existente en C++ sin cambiar su significado original.
Ejemplo:
en un;
flotante b,suma;
suma=a+b;
Aquí, las variables «a» y «b» son de tipo «int» y «float», que son tipos de datos integrados. Por lo tanto, el operador de suma ‘+’ puede sumar fácilmente los contenidos de «a» y «b». Esto se debe a que el operador de suma “+” está predefinido para agregar variables de tipo de datos incorporado únicamente.
Ahora, considere otro ejemplo
clase A
{
};
int principal()
{
A a1,a2,a3;
a3= a1 + a2;
devolver 0;
}
En este ejemplo tenemos 3 variables “a1”, “a2” y “a3” de tipo “clase A”. Aquí estamos tratando de agregar dos objetos «a1» y «a2», que son del tipo definido por el usuario, es decir, del tipo «clase A» usando el operador «+». Esto no está permitido porque el operador de suma «+» está predefinido para operar solo en tipos de datos integrados. Pero aquí, la «clase A» es un tipo definido por el usuario, por lo que el compilador genera un error. Aquí es donde entra el concepto de “Sobrecarga del operador”.
Ahora, si el usuario quiere hacer que el operador «+» agregue dos objetos de clase, el usuario tiene que redefinir el significado del operador «+» para que agregue dos objetos de clase. Esto se hace utilizando el concepto “Sobrecarga de operadores”. Entonces, la idea principal detrás de la «sobrecarga de operadores» es usar operadores c ++ con variables de clase u objetos de clase. Redefinir el significado de los operadores realmente no cambia su significado original; en cambio, se les ha dado un significado adicional junto con los existentes.
Un ejemplo sencillo y completo.
CPP
#include<iostream> using namespace std; class Complex { private: int real, imag; public: Complex(int r = 0, int i = 0) {real = r; imag = i;} // This is automatically called when '+' is used with // between two Complex objects Complex operator + (Complex const &obj) { Complex res; res.real = real + obj.real; res.imag = imag + obj.imag; return res; } void print() { cout << real << " + i" << imag << '\n'; } }; int main() { Complex c1(10, 5), c2(2, 4); Complex c3 = c1 + c2; c3.print(); }
12 + i9
Producción:
12 + i9
¿Cuál es la diferencia entre funciones de operador y funciones normales?
Las funciones del operador son las mismas que las funciones normales. Las únicas diferencias son que el nombre de una función de operador es siempre la palabra clave del operador seguida del símbolo del operador y las funciones de operador se llaman cuando se usa el operador correspondiente.
A continuación se muestra un ejemplo de una función de operador global.
12 + i9
¿Podemos sobrecargar todos los operadores?
Casi todos los operadores se pueden sobrecargar excepto unos pocos. A continuación se muestra la lista de operadores que no se pueden sobrecargar.
sizeof typeid Scope resolution (::) Class member access operators (.(dot), .* (pointer to member operator)) Ternary or conditional (?:)
Operadores que se pueden sobrecargar
podemos sobrecargar
- Operadores unarios
- Operadores binarios
- Operadores especiales ( [ ],() etc.)
Pero, entre ellos, hay algunos operadores que no se pueden sobrecargar. Están
- Operador de resolución de alcance : :
- Operador de selección de miembros
- Selección de miembros a través de *
Puntero a variable miembro
- Operador condicional? :
- tamaño del operador tamaño de()
Operadores que se pueden sobrecargar
- Aritmética binaria -> +, -, *, /, %
- Aritmética unaria -> +, -, ++, —
- Asignación -> =, +=,*=, /=,-=, %=
- Bit a bit -> & , | , << , >> , ~ , ^
- Desreferenciar -> (->)
- Asignación de memoria dinámica y desasignación -> Nuevo, eliminar
- Subíndice -> [ ]
- Llamada de función ->()
- Lógico -> &, | |, !
- Relacional -> >, < , = =, <=, >=
¿Por qué no se pueden sobrecargar los operadores mencionados anteriormente?
1. sizeof: devuelve el tamaño del objeto o tipo de datos ingresado como operando. Esto lo evalúa el compilador y no se puede evaluar durante el tiempo de ejecución. El incremento adecuado de un puntero en una array de objetos se basa implícitamente en el operador sizeof. Alterar su significado mediante la sobrecarga provocaría el colapso de una parte fundamental del lenguaje.
2. typeid: proporciona a un programa CPP la capacidad de recuperar el tipo derivado real del objeto al que hace referencia un puntero o una referencia. Para este operador, el objetivo es identificar de forma única un tipo. Si queremos hacer que un tipo definido por el usuario se ‘parezca’ a otro tipo, se puede usar el polimorfismo, pero el significado del operador typeid debe permanecer inalterado o, de lo contrario, podrían surgir problemas graves.
3. Resolución de alcance (::): Esto ayuda a identificar y especificar el contexto al que se refiere un identificador al especificar un espacio de nombres. Se evalúa completamente en tiempo de ejecución y funciona con nombres en lugar de valores. Los operandos de resolución de alcance son expresiones de nota con tipos de datos y CPP no tiene sintaxis para capturarlos si estuviera sobrecargado. Por lo tanto, es sintácticamente imposible sobrecargar este operador.
4. Operadores de acceso a miembros de clase (.(punto), .* (apuntador a operador miembro)): La importancia y el uso implícito de los operadores de acceso a miembros de clase pueden entenderse a través del siguiente ejemplo:
C++
#include <iostream> using namespace std; class ComplexNumber{ private: int real; int imaginary; public: ComplexNumber(int real, int imaginary){ this->real = real; this->imaginary = imaginary; } void print(){ cout<<real<<" + i"<<imaginary; } ComplexNumber operator+ (ComplexNumber c2){ ComplexNumber c3(0,0); c3.real = this->real+c2.real; c3.imaginary = this->imaginary + c2.imaginary; return c3; } }; int main() { ComplexNumber c1(3,5); ComplexNumber c2(2,4); ComplexNumber c3 = c1 + c2; c3.print(); return 0; }
5 + i9
El enunciado NúmeroComplejo c3 = c1 + c2; se traduce internamente como ComplexNumber c3 = c1.operator+ (c2); para invocar la función del operador. El argumento c1 se pasa implícitamente usando el ‘.’ operador. La siguiente instrucción también utiliza el operador punto para acceder a la función miembro imprimir y pasar c3 como argumento. Por lo tanto, para garantizar un sistema confiable y no ambiguo de acceso a los miembros de la clase, el mecanismo predefinido que utiliza operadores de acceso a los miembros de la clase es absolutamente esencial. Además, estos operadores también trabajan con nombres y no con valores y no existe ninguna disposición (sintácticamente) para sobrecargarlos.
5. Ternario o condicional (?:): El operador ternario o condicional es una representación abreviada de una instrucción if-else. En el operador, las expresiones verdadero/falso solo se evalúan sobre la base del valor de verdad de la expresión condicional.
sentencia condicional ? expresión1 (si la afirmación es VERDADERA): expresión2 (si no)
Una función que sobrecarga el operador ternario para una clase, digamos ABC usando la definición
Operador ABC ?: (condición booleana, ABC trueExpr, ABC falseExpr);
no podría garantizar que solo una de las expresiones fue evaluada. Por lo tanto, el operador ternario no puede sobrecargarse.
Puntos importantes sobre la sobrecarga de operadores
1) Para que la sobrecarga de operadores funcione, al menos uno de los operandos debe ser un objeto de clase definido por el usuario.
2) Operador de asignación: el compilador crea automáticamente un operador de asignación predeterminado con cada clase. El operador de asignación predeterminado asigna todos los miembros del lado derecho al lado izquierdo y funciona bien en la mayoría de los casos (este comportamiento es el mismo que el del constructor de copias). Vea esto para más detalles.
3) Operador de conversión: también podemos escribir operadores de conversión que se pueden usar para convertir un tipo a otro tipo.
CPP
#include <iostream> using namespace std; class Fraction { private: int num, den; public: Fraction(int n, int d) { num = n; den = d; } // Conversion operator: return float value of fraction operator float() const { return float(num) / float(den); } }; int main() { Fraction f(2, 5); float val = f; cout << val << '\n'; return 0; }
0.4
Los operadores de conversión sobrecargados deben ser un método miembro. Otros operadores pueden ser el método miembro o el método global.
4) Cualquier constructor al que se pueda llamar con un solo argumento funciona como un constructor de conversión, lo que significa que también se puede usar para la conversión implícita a la clase que se está construyendo.
CPP
#include <iostream> using namespace std; class Point { private: int x, y; public: Point(int i = 0, int j = 0) { x = i; y = j; } void print() { cout << "x = " << x << ", y = " << y << '\n'; } }; int main() { Point t(20, 20); t.print(); t = 30; // Member x of t becomes 30 t.print(); return 0; }
x = 20, y = 20 x = 30, y = 0
Pronto discutiremos la sobrecarga de algunos operadores importantes como new, delete, comma, function call, arrow, etc.
Cuestionario sobre la sobrecarga de operadores
Referencias:
http://en.wikipedia.org/wiki/Operator_overloading
Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.
Publicación traducida automáticamente
Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA