Programación II. Guía 7 1 Facultad: Ingeniería Escuela: Computación Asignatura: Programación II Tema: Sobrecarga de Operadores. Objetivos Describir como redefinir (sobrecargar) operadores para que funcionen con nuevos tipos de datos. Diferenciar sobrecarga de operadores binarios y unarios. Identificar cuando sobrecargar operadores. Implementar programas en C++ que sobrecarguen operadores unarios. Materiales y Equipo • Computadora con el software DevC++ • Guía Número 7 Introducción Recordemos que los operadores son un tipo de tokens que indican al compilador la realización de determinadas operaciones sobre variables u otros objetos (los operandos). Por ejemplo, cuando encontramos una expresión del tipo: z = x + y; // suponemos que x, y, z son de tipo int (1) Sabemos que la sentencia contiene dos operadores; el de suma (+) y el de asignación (=); que estos operadores actúan (operan) sobre objetos de tipo int, y que sus reglas de uso y su significado (que resultados se obtienen) están perfectamente definidos en el lenguaje. Los operadores aceptan uno o varios operandos de tipo específico (alguno de los tipos básicos preconstruidos en el lenguaje), produciendo y/o modificando un valor de acuerdo con ciertas reglas. Sin embargo, C++ permite redefinir la mayor parte de ellos. Es decir, permite que puedan aceptar otro tipo de operandos (distintos de los tipos básicos) y seguir otro comportamiento, al 2 Programación II, Guía 7 tiempo que conservan el sentido y comportamiento originales cuando se usan con los operandos normales. Esta posibilidad, que recibe el nombre de sobrecarga del operador, es una consecuencia del polimorfismo y hace posible que en la expresión: c = d + e; // suponemos que c, d, e son de tipo UnaClase (2) el sentido de la suma y el de asignación sean totalmente específicos y distintos que el de la expresión “1” anterior cuando se utilizaron los mismos operadores con operandos tipo int. NOTA: La primera e importantísima advertencia es que la sobrecarga se refiere y tiene aplicación solo cuando los operandos son instancias de clases. Es decir, no es posible modificar el sentido de la suma y asignación de la expresión “2” si c, d y e fuesen enteros o de cualquiera de los tipos básicos preconstruidos en el lenguaje. La sobrecarga de los operadores es una de las características más interesantes de C++ y, naturalmente, de la programación orientada a objetos. La sobrecarga hace posible manipular objetos de clases con operadores estándar tales como +, *, [ ] y <<. Esta propiedad de los operadores permite redefinir el lenguaje C++, que puede crear nuevas definiciones de operadores, La sobrecarga es una característica que ofrece el lenguaje C++ para aplicar una misma operación, a través de operadores o funciones, a diferentes tipos de datos. Se pueden sobrecargar los operadores predefinidos y funciones definidas por el usuario. La sobrecarga permite generalizar el uso de operadores y funciones. Sobrecarga de Operadores. Es el proceso de asignar dos ó más operaciones al mismo operador. En otras palabras, permite asignar una o más funciones adicionales a un operador estándar, con el fin de que ésta sea llamada según el contexto en el cual se utilice el operador. Un operador sobrecargado no puede tener parámetros predeterminados. Para sobrecargar un operador, y aplicarlo en operaciones entre clases, se usa la función especial operador. Una función operador es una función cuyo nombre consta de la palabra reservada operator seguida de un operador unitario o binario que se sobrecargará. Programación II. Guía 7 3 La sintaxis para sobrecargar un operador es la siguiente: <tipo de dato> <Nombre de clase> operator <#> (lista de parámetros) { //instrucciones que forman el cuerpo del operador } Donde: • tipo de dato: indica el tipo de dato que produce el operador sobrecargado, por lo general es la referencia del objeto. • Nombre de clase: es el nombre de clase en la que estamos aplicando sobrecarga. • operator: es una palabra reservada utilizada para realizar la sobrecarga • #: operador que estamos sobrecargando. • Lista de parámetros: es la lista de objetos (en referencia) o variables que se procesarán en la sobrecarga, si la operación es unaria no tendrá parámetros, si la operación es binaria tendrá un parámetro. Operadores sobrecargables. El lenguaje C++ permite redefinir la funcionalidad de los siguientes operadores: + - >> * / % >>= <<= == ^ & ~ != <= >= ! = && < || ++ > += -= -- ->* *= , -> /= %= [] () ^= new &= |= << new[] delete delete[] Los operadores +, -, * y & son sobrecargables en sus dos versiones, unaria y binaria. Es decir: suma binaria +; más unitario +; multiplicación *; indirección *; referencia & y manejo de bits &. Es notable que C++ ofrezca casos de operadores sobrecargados incluso en su Librería Estándar. Por ejemplo, los operadores == y != para la clase type_info. Sin embargo, la posibilidad de sobrecarga no se extiende a todos los operadores. Excepciones: En la lista anterior, puede verificarse que todos los operadores pueden ser sobrecargados, incluyendo new, new [ ], delete y delete [ ], excepto los siguientes: • Selector directo de componente . • Operador de indirección de puntero a miembro .* • Operador de acceso a ámbito :: • Condicional ternario ?: Programación II, Guía 7 4 • Directivas de preprocesado # y # # • sizeof: informa del tamaño de almacenamiento utilizado por cualquier objeto, sea un tipo básico o derivado. • typeid: identifica un operador con el que puede obtenerse el tipo de objetos y expresiones en tiempo de ejecución. Permite comprobar si un objeto es de un tipo particular, y si dos objetos son del mismo tipo. Sobrecarga de Operadores Binarios. Un operador binario tiene dos operandos o argumentos, uno a cada lado del operador. El operador “/”, por ejemplo, es binario. Algunos operadores tales como “+” son unitarios y binarios, dependiendo de su uso. Los operadores binarios pueden ser sobrecargados de dos formas: a. Declarando una función miembro no estática que acepte un argumento b. Declarando una función no miembro (generalmente friend) que acepte dos argumentos. Según lo anterior, y dependiendo de la declaración, si @ representa el operador binario, la expresión x @ y entre miembros de una clase C puede ser interpretada como cualquiera de las dos formas: a. x.operator @ (y) b. operator @ (x, y) Nota: recuerde la versión a parece recibir solo un argumento, pero en realidad recibe dos si se considera el argumento implícito this, de forma que podría considerarse: x.operator @ (C * this, y) siendo: C * this = &x En otras palabras, un operador binario se puede sobrecargar pasando a la función dos argumentos si no es función miembro. El primer argumento es el operando izquierdo del operador sobrecargado y el segundo argumento es el operando derecho, si la función no es miembro de la clase. Consideremos un tipo de clase T y dos objetos x1 y x2 de tipo T Definamos un operador binario “+” sobrecargado, entonces: x1 + x2 se interpreta como: operator + (x1, x2) Programación II. Guía 7 5 Un operador binario puede, por consiguiente, ser definido así: • Como una función de dos argumentos • Como una función miembro de un argumento (el caso más frecuente) • Nunca las dos a la vez. Sobrecarga de los operadores de manejo de flujos de entrada “>>” y de flujos de salida “<<” Binarios. Un caso especial es la sobrecarga de los operadores “<<” y “>>” utilizados en la salida u entrada de datos respectivamente. Estos operadores se encuentran definidos en la biblioteca iostream de C++. En el caso de la entrada de datos del teclado a la aplicación, se establece una relación entre una referencia a un objeto de la clase istream y una referencia a un objeto de la clase en la cual se está incluyendo la sobrecarga. La sintaxis es la siguiente: friend istream & operator >> (istream &, <Nombre de clase> &); En el caso de la salida de datos de la aplicación a la pantalla, se establece una relación entre una referencia a un objeto de la clase ostream y una referencia a un objeto de la clase en la cual se está incluyendo la sobrecarga. La sintaxis es la siguiente: friend ostream & operator << (ostream &, <Nombre de clase> &); Observe que las funciones que se obtienen al sobrecargar los operadores se declaran como amigas (friend) de la clase en la cual se insertaron. Esto es para que dichas funciones, externas a la clase, puedan tener acceso a los miembros privados de la misma. Procedimiento Ejemplo 1: En C++, el operador “+” no puede utilizarse para concatenar cadenas. Sin embargo, si utilizamos una clase propia llamada Cadena, podríamos sobrecargar el operador “+” para realizar la concatenación de cadenas, como se muestra en el siguiente ejemplo: 6 Programación II, Guía 7 #include <iostream> #include <string.h> // Para las funciones de manejo de cadenas: strcpy, strlen y strcat using namespace std; class Cadena { private: int longitud; char *ptrCadena; // longitud de la cadena // Puntero a la cadena de caracteres public: Cadena (char [ ]= ""); Cadena (Cadena &); ~Cadena(); int long_cadena (); //constructor predeterminado //constructor por copia //destructor //devuelve longitud de la cadena // Funciones para sobrecarga de operadores Cadena & operator + (Cadena &); //concatenación /* Sobrecarga de los operadores de manejo de flujos de entrada ">>" y de flujos de salida "<<" */ friend ostream & operator << (ostream &, Cadena &); friend istream & operator >> (istream &, Cadena &); }; Cadena :: Cadena (char * cadena) //Constructor por defecto { longitud = strlen(cadena); ptrCadena = new char[longitud + 1]; // Creación dinámica if(ptrCadena == NULL) { cout<<"No hay memoria"; exit(0); } strcpy(ptrCadena, cadena); // Se verifica si hay memoria disponible // Se copia el contenido de cadena en el objeto creado } Cadena :: Cadena (Cadena & copia) { longitud = strlen(copia.ptrCadena); ptrCadena = new char[longitud + 1]; //Constructor copia if(ptrCadena == NULL) // Se verifica si hay memoria disponible { cout<<"No hay memoria"; exit(0); } strcpy(ptrCadena, copia.ptrCadena); /* Se copia el contenido de copia en el objeto creado*/ } Programación II. Guía 7 7 Cadena :: ~Cadena() { delete [ ] ptrCadena; } // Sobrecarga del operador "+" para que permita concatenar cadenas Cadena & Cadena :: operator + (Cadena & s) { char *temps = ptrCadena; longitud += s.longitud; ptrCadena = new char[longitud+1]; if(ptrCadena == NULL) { cout<<"No hay memoria"; exit(0); } // Se verifica si hay memoria disponible strcpy(ptrCadena, temps); // Se copia el contenido de temps en el objeto creado strcat(ptrCadena, s.ptrCadena); /* Se realiza la concatenación del contenido de s con el objeto creado*/ delete [ ] temps; // Se libera memoria reservada con new return *this; } int Cadena :: long_cadena () { return longitud; } // Sobrecarga del operador de entrada ">>" ostream & operator<< (ostream & salida, Cadena & s) { salida<< s.ptrCadena; return salida; } // Sobrecarga del operador de salida "<<" istream & operator>> (istream & entrada, Cadena & s) { entrada>> s.ptrCadena; return entrada; } int main() { char cad1[20], cad2[20]; system("cls"); cout << "Introducir primer cadena: "; gets(cad1); cout << "Introducir segunda cadena: "; gets(cad2); Cadena s1(cad1), s2(cad2), s3; //Creamos los objetos de tipo cadena 8 Programación II, Guía 7 //probando operador sobrecargado de concatenación cout << endl << endl << "Resultado de concatenar Cadenas s1 y s2: " << endl; s3 = s1 + s2; cout << "s3: " << s3; //prueba el constructor de copiado Cadena *s4= new Cadena(s2); cout << endl << endl << "Constructor copia para *s4: " << *s4; //prueba del operador de asignación = cout << endl << endl << "Asignando s3 a *s4, *s4: "; *s4 = s3; cout << *s4 caracteres."; << endl << endl << "Longitud de s4: " << s4 -> long_cadena() << " system("pause>nul"); return 0; } Ejemplo 2: En este ejemplo se sobrecargan los operadores aritméticos de la suma y la resta, de tal manera que puedan operar datos de tipo Vector. #include <iostream> using namespace std; class Vector { private: float CoordX; float CoordY; // Coordenada en eje x del vector // Coordenada en eje y del vector public: Vector (float Va1 = 0, float Val2 =0); //constructor predeterminado ~Vector(){ }; //destructor void ImprimeVector (); //Muestra el vector // Funciones para sobrecarga de operadores Vector operator + (Vector Vec); //Sobrecarga del operador "+" para realizar la //suma de vectores Vector operator - (Vector Vec); //Sobrecarga del operador "-" para realizar la //resta de vectores }; /* Método constructor con parámetros predeterminados; a los cuales, si no se les especifican otros valores, se les asignará 0 */ Programación II. Guía 7 9 Vector :: Vector(float Val1, float Val2) { CoordX = Val1; CoordY = Val2; } // Método que imprime los valores de los atributos del vector void Vector :: ImprimeVector( ) { cout << "La componente en X del vector es: " << CoordX << endl; cout << "La componente en Y del vector es: " << CoordY << endl; } /* Método en el cual se sobrecarga el operador "+"; por lo tanto se podrá utilizar para la suma aritmética, como para suma de vectores. Lo anterior da como resultado un objeto de tipo Vector */ Vector Vector :: operator + (Vector Vec) { return Vector(CoordX + Vec.CoordX, CoordY + Vec.CoordY); } /* Método en el cual se sobrecarga el operador "-"; por lo tanto se podrá utilizar para la resta aritmética, como para resta de vectores. Lo anterior da como resultado un objeto de tipo Vector */ Vector Vector :: operator - (Vector Vec) { return Vector(CoordX - Vec.CoordX, CoordY - Vec.CoordY); } int main() { system("cls"); Vector Vec1(3,1), Vec2(1,2), Vec3; //Creamos los objetos de tipo Vector // Se invoca al operador "+" sobrecargado, es decir, se realiza la suma de vectores Vec3 = Vec1 + Vec2; // Mostramos el vector resultante cout << endl << endl << "Los datos del vector resultante de la suma de vectores son: " << endl; Vec3.ImprimeVector(); // Se invoca al operador "-" sobrecargado, es decir, se realiza la resta de vectores Vec3 = Vec1 - Vec2; // Mostramos el vector resultante cout << endl << endl << "Los datos del vector resultante de la resta de vectores son: " << endl; Vec3.ImprimeVector(); system("pause>nul"); return 0; } 10 Programación II, Guía 7 Análisis de Resultados Ejercicio 1: Modifique el código del ejemplo No. 2, de tal manera que la solución se maneje a través de un menú que contenga las siguientes opciones: a) Crear los objetos de tipo vector, solicitando los datos al usuario. b) Realizar la suma de vectores. c) Realizar la resta de vectores. d) Realizar el producto escalar de vectores. e) Salir de la aplicación. El programa debe estar debidamente comentado. Ejercicio 2: Tomar el código del ejemplo No. 1 como referencia y realizar los cambios que sean necesarios para crear un programa que provea la funcionalidad necesaria para trabajar con cadenas de caracteres. La funcionalidad debe implementarse con un menú con las siguientes opciones: Opción de menú 1: Ingresar las cadenas de caracteres (como mínimo dos). Opción de menú 2: Concatenar cadenas. Opción de menú 3: Comparación de cadenas de caracteres. Considerar la sobrecarga de los operadores ==, <, >, != Opción de menú 4: Determinar si una cadena está vacía. Considerar la sobrecarga del operador “ ! “ Opción de menú 5: Acceder a un carácter individual en una cadena, es decir, realizar una Opción de menú 6: Determinar la longitud de una cadena. Opción de menú 7: Salir del programa. El menú deberá estar siempre activo, en la misma posición en pantalla, hasta que el usuario seleccione la opción salir. El programa debe estar debidamente comentado. Se sugiere utilizar la declaración de la clase Cadena mostrada a continuación: Programación II. Guía 7 11 class Cadena { private: int longitud; // longitud de la cadena char *ptr; // Puntero a la cadena de caracteres public: Cadena (char []= ""); //constructor predeterminado Cadena (Cadena &); //constructor por copia ~Cadena(); //destructor int long_cadena (void); //devuelve longitud de la cadena // Funciones para sobrecarga de operadores bool operator == (Cadena &); //prueba si s1 = s2 bool operator != (Cadena &); //prueba si s1 != s2 bool operator ! ( ); //prueba si Cadena esta vacía bool operator < (Cadena &); //prueba si s1 < s2 bool operator > (Cadena &); //prueba si s1 > s2 Cadena & operator + (Cadena &); //concatenación char operator[] (int); //operador de subíndice /* Sobrecarga de los operadores de manejo de flujos de entrada ">>" y de flujos de salida "<<" */ friend ostream & operator<< (ostream &, Cadena &); friend istream & operator>> (istream &, Cadena &); }; 12 Programación II, Guía 7 Hoja de cotejo: Guía 7: Sobrecarga de Operadores. Alumno: Máquina No: Docente: GL: 7 Fecha: EVALUACIÓN % CONOCIMIENTO Del 20 al 30% APLICACIÓN DEL CONOCIMIENTO Del 40% al 60% 1-4 5-7 8-10 Conocimiento deficiente de los fundamentos teóricos Conocimiento y explicación incompleta de los fundamentos teóricos Conocimiento completo y explicación clara de los fundamentos teóricos No tiene actitud proactiva. Actitud propositiva y con propuestas no aplicables al contenido de la guía. Tiene actitud proactiva y sus propuestas son concretas. ACTITUD Del 15% al 30% TOTAL 100% Nota