5/21/2018 19 PROGRAMACION LENGUAJE C++
1/34
2.1.2 Sobrecarga del operador predecremento --@
Como ejemplo incluimos una versin de la clase anterior en la que sobrecargamos los
operadorespreincrementoypredecremento, pero utilizando la posibilidad2.1benunciada alprincipio . Es decir, mediante una funcin-operador externa que acepte un argumento.
Nota: aunque no es necesario, porque la nica propiedad de la clase es pblica, hemos
declarado las funciones-operador comofriendde la clase. Esto es lo usual, porque as segarantiza el acceso a los miembros, incluso privados, de la clase desde la funcin.
#include using namespace std;
class Entero {public: int x;
friend Entero& operator++(Entero&);friend Entero& operator--(Entero&);};Entero& operator++ (Entero& e) {e.x = e.x + e.x;return e;
}Entero& operator-- (Entero& e) {e.x = e.x / 2;return e;
}
void main () { // ==============
Entero e1, e2, e3;e1.x = 5;e3 = ++e2 = ++e1;cout
5/21/2018 19 PROGRAMACION LENGUAJE C++
2/34
2.2a. Declarando una funcin miembro no esttica que acepte un entero comoargumento. Ejemplo:
C C::operator++(int);
2.2b. Declarando una funcin no miembro (generalmentefriend) que acepte un objeto de la clase
y un entero como argumentos (en este orden). Ejemplo:
C operator-- (C&, int);
Segn lo anterior, y dependiendo de la declaracin, si @representa un post-operador unitario(++o--), la expresinx@puede ser interpretada como cualquiera de las dos formas:
2.2a: x.operator@(int)
2.2b: operator@(x, int)
Nota: debemos advertir que la inclusin del entero como argumento es simplemente un
recurso de los diseadores del lenguaje para que el compilador pueda distinguir lasdefiniciones de los "pre" y "post" operadores++y--(el argumento no se usa para nada ms).De hecho, las primeras versiones C++ (hasta la versin 2.0 del preprocesador cfront deAT&T), no distinguan entre las versiones sobrecargadas "pre" y "post" de los operadoresincremento y decremento.
Para ilustrar el proceso, extendemos el ejemplo de la clase Enterosobrecargando losoperadorespostincrementoypostdecremento. Mantenemos la misma lgica que establecimoscon los preoperadores: el incremento aumenta al doble el valor de la propiedad x, y el decrementolo disminuye a la mitad. Para la definicin del primero utilizamos la solucin2.2a, y la2.2bpara elsegundo.
#include using namespace std;
class Entero {public: int x;friend Entero& operator++(Entero&); // L.6: preincrementofriend Entero& operator--(Entero&); // L.7: predecrementoEntero operator++(int i) { // L.8: postincrementoEntero tmp = *this; // L.9:x = x + x; return tmp;
}friend Entero operator--(Entero&, int); // L.12: postdecremento
};
Entero& operator++ (Entero& e) { // preincrementoe.x = e.x + e.x; return e;
}Entero& operator-- (Entero& e) { // predecremento
e.x = e.x / 2; return e;}Entero operator-- (Entero& e, int i) { // L.21: postdecrementoEntero tmp = e; // L.22:e.x = e.x / 2; return tmp;
5/21/2018 19 PROGRAMACION LENGUAJE C++
3/34
}
void main () { // ===========Entero e1 = { 6 }, e2; // M.1e2 = e1++; // M.2cout
5/21/2018 19 PROGRAMACION LENGUAJE C++
4/34
cuando la funcin-operador no es miembro de la clase.
Ntese que en este ltimo caso, el objeto pasado como argumento debe serlo como referencia(L.12 y L.21). La razn es que la funcin-operador debe modificar el valor del objeto pasado como
argumento ( 4.2.3).
Observe tambin que este diseo permite la existencia de varias funciones-operador post-incremento / post-decremento referidas a clases distintas. El mecanismo de sobrecarga permitir alcompilador saber de que funcin se trata a travs del anlisis de sus argumentos.
Nota: el diseo de los operadores "post" presenta una importante dificultad terica: en amboscasos es necesario devolver un valor, no una referencia. Esto hace que el resultado no pueda serutilizado al lado izquierdo de una asignacin (como un Lvalue). Es decir, no son posiblesasignaciones del tipo:
e2++ = e1; // Error: "Lvalue required"
por tanto tampoco son posibles expresiones de asignacin compuesta del tipo:
e3 = e2++ = e1++; // Error:
que s son posibles con los pre-operadores . Esta limitacin es extensiva incluso a los tiposbsicos; tampoco con ellos son posibles expresiones del tipo:
int x = 5, y, z;y++ = x; // Error: "Lvalue required"z = y++ = x++; // Error:
mientras que las anlogas con preincremento y predecremento s son posibles :
z = ++y = ++x; // Ok.
En la pgina adjuntase expone un ejemplo de una tcnica que utiliza la indireccin ( 4.9.16)y la sobrecarga de operadores unarios para simular una sobrecarga de los operadores ++y--
sobre punteros; algo que como se ha indicado ( 4.9.18), no es en principio posible Ejemplo.
3 Sobrecarga del operador de indireccin
Recordemos que el operador de indireccin*( 4.9.11)es un preoperador unario, por lo que su
sobrecarga puede ser efectuada de cualquiera de las formas2.1ao2.1b . En la pgina adjunta
se incluye un completo ejemplo de su utilizacin ( Ejemplo).
4 Sobrecarga del operador de negacin lgica
http://www.zator.com/Cpp/E4_2_3.htmhttp://www.zator.com/Cpp/E4_2_3.htmhttp://www.zator.com/Cpp/E4_2_3.htmhttp://www.zator.com/Cpp/E4_9_16.htm#Selector%20indirecto%20de%20miembrohttp://www.zator.com/Cpp/E4_9_16.htm#Selector%20indirecto%20de%20miembrohttp://www.zator.com/Cpp/E4_9_16.htm#Selector%20indirecto%20de%20miembrohttp://www.zator.com/Cpp/E4_9_18.htm#Limitacioneshttp://www.zator.com/Cpp/E4_9_18.htm#Limitacioneshttp://www.zator.com/Cpp/E4_9_18.htm#Limitacioneshttp://www.zator.com/Cpp/E4_9_18cw1.htmhttp://www.zator.com/Cpp/E4_9_18cw1.htmhttp://www.zator.com/Cpp/E4_9_18cw1.htmhttp://www.zator.com/Cpp/E4_9_11.htm#4.9.11a%20Operador%20de%20indirecci%C3%B3nhttp://www.zator.com/Cpp/E4_9_11.htm#4.9.11a%20Operador%20de%20indirecci%C3%B3nhttp://www.zator.com/Cpp/E4_9_11.htm#4.9.11a%20Operador%20de%20indirecci%C3%B3nhttp://www.zator.com/Cpp/E4_12_2w5.htmhttp://www.zator.com/Cpp/E4_12_2w5.htmhttp://www.zator.com/Cpp/E4_12_2w5.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_12_2w5.htmhttp://www.zator.com/Cpp/E4_9_11.htm#4.9.11a%20Operador%20de%20indirecci%C3%B3nhttp://www.zator.com/Cpp/E4_9_18cw1.htmhttp://www.zator.com/Cpp/E4_9_18.htm#Limitacioneshttp://www.zator.com/Cpp/E4_9_16.htm#Selector%20indirecto%20de%20miembrohttp://www.zator.com/Cpp/E4_2_3.htm5/21/2018 19 PROGRAMACION LENGUAJE C++
5/34
La sobrecarga del operador! NOT de negacin lgica puede verse en el epgrafe ( 4.9.18g)
junto con las sobrecargas del resto de operadores lgicos (binarios).
4.9.18d Sobrec arga del operador [ ]
1 Sinopsis
Recordemos ( 4.9.16)que este operador sirve para sealar subndices de matrices simples ymultidimensionales; de ah su nombre, operadorsubndiceo deseleccin de miembro de matriz.
La expresin:
[exp2]
se define como:*((exp1) + (exp2))dondeexp1es un puntero yexp2es un entero oviceversa.
Por ejemplo, arrX[3]se define como:*(arrX + 3)o*(3 + arrX), dondearrXes un
puntero al primer elemento de la matriz.(arrX + 3)es un puntero al cuarto elemento, y*(arrX
+ 3)es el valor del cuarto elemento de la matriz.
Lo anterior puede sintetizarse en la siguiente relacin:
arrX[3] == *(arrX + 3) // 1a
2 Cuando se utiliza con tipos definidos por el usuario, este operador puede ser sobrecargado
mediante la funcinoperator[ ]( ) ( 4.9.18).Para ilustrarlocon un ejemplo, utilizaremos laclasemVectorque contiene una matriz (es una matriz de vectores), y suponemos que loselementos de la matriz son vectores deslizantes de un espacio bidimensional. El diseo bsico esel que se indica:
class Vector { // definicin de la clase Vectorpublic: int x, y;
};
class mVector { // definicin de la clase mVectorpublic:Vector* mVptr; // L.6:mVector(int n = 1) { // constructor por defectomVptr = new Vector[n]; // L.8:
}~mVector() { // destructordelete [] mVptr;
}};
http://www.zator.com/Cpp/E4_9_18g.htmhttp://www.zator.com/Cpp/E4_9_18g.htmhttp://www.zator.com/Cpp/E4_9_18g.htmhttp://www.zator.com/Cpp/E4_9_16.htm#Operador%20de%20elemento%20de%20matrizhttp://www.zator.com/Cpp/E4_9_16.htm#Operador%20de%20elemento%20de%20matrizhttp://www.zator.com/Cpp/E4_9_16.htm#Operador%20de%20elemento%20de%20matrizhttp://www.zator.com/Cpp/E4_9_18.htm#funci%C3%B3n%20operadorhttp://www.zator.com/Cpp/E4_9_18.htm#funci%C3%B3n%20operadorhttp://www.zator.com/Cpp/E4_9_18.htm#funci%C3%B3n%20operadorhttp://www.zator.com/Cpp/E4_9_18.htm#funci%C3%B3n%20operadorhttp://www.zator.com/Cpp/E4_9_16.htm#Operador%20de%20elemento%20de%20matrizhttp://www.zator.com/Cpp/E4_9_18g.htm5/21/2018 19 PROGRAMACION LENGUAJE C++
6/34
Comentario
La claseVectortiene solo dos miembros, que suponemos las componentes escalares de cadavector del plano. Por simplicidad hemos supuesto que son int, pero podran ser otros tipos depunto flotante, por ejemplofloatodouble. Esta clase auxiliar la hemos definido externa eindependiente de la clase
mVector.
Tambin podra utilizarse otro diseo en el queVectorestuviese definida "dentro" de la
clasemVector. Las diferencias entre ambos y los criterios de uso se discuten en ( 4.13.2):
class mVector { // definicin de la clase mVector...class Vector { // clase anidada
...};...
};
La clasemVectortiene un solo miembro; un puntero-a-VectormVptr(L.6). Tambin definimos unconstructor por defecto y un destructor.
Observe (L.8) que el constructor del objeto tipomVector, crea una matriz de objetos
tipoVectordel tamao indicado en el argumento (1 por defecto) y la seala con el
punteromVptr. Esta matriz est alojada en memoria persistente ( 4.9.20c)y en cierta formapodramos pensar que es "externa" al objeto, ya que este realmente solo contiene un puntero [1].
Precisamente en razn de esta persistencia, el destructor debe rehusar la memoria asignada a la
matriz, pues de otro modo este espacio se perdera al ser destruido el objeto ( 4.9.21).
Siguiendoel paradigma de la POO, esta clase deber contener los datos (la matriz) y losalgoritmos (mtodos) para manejarla. Deseamos utilizar los objetos de tipomVectorcomoautnticas matrices, por lo que deberamos poder utilizarlos con lgebra de matrices C++.
Utilizando una analoga, si por ejemplomes una matriz de enteros, sabemos que el lenguaje nospermite utilizar las expresiones siguientes:
m[i]; // L.1: acceso a elemento con el operador subndiceint x = m[i]; // L.2: asignacin a un miembro de la clase intm[i] = m[j]; // L.3: asignacin a miembrom[i] = 3 * m[j]; // L.4: producto por un escalarm[i] = m[j] * m[k]; // L.5: producto entre miembros
En consecuencia, debemos preparar el diseo de la clasemVectorde forma que que puedamimetizarse el comportamiento anterior con sus objetos. Es decir, deben permitirse las siguientesexpresiones:
mVector m1;m1[i]; // acceso a elemento con el operador subndiceVector v1 = m1[i]; // asignacin a un miembro de la clase Vectorm1[i] = m1[j]; // asignacin a miembrom1[i] = 3 * m1[j]; // producto por un escalarm1[i] = m1[j] * m1[k]; // producto entre miembros
http://www.zator.com/Cpp/E4_13_2.htmhttp://www.zator.com/Cpp/E4_13_2.htmhttp://www.zator.com/Cpp/E4_13_2.htmhttp://www.zator.com/Cpp/E4_9_20c.htmhttp://www.zator.com/Cpp/E4_9_20c.htmhttp://www.zator.com/Cpp/E4_9_20c.htmhttp://www.zator.com/Cpp/E4_9_18d.htm#[1]http://www.zator.com/Cpp/E4_9_18d.htm#[1]http://www.zator.com/Cpp/E4_9_18d.htm#[1]http://www.zator.com/Cpp/E4_9_21.htmhttp://www.zator.com/Cpp/E4_9_21.htmhttp://www.zator.com/Cpp/E4_9_21.htmhttp://www.zator.com/Cpp/E4_9_21.htmhttp://www.zator.com/Cpp/E4_9_18d.htm#[1]http://www.zator.com/Cpp/E4_9_20c.htmhttp://www.zator.com/Cpp/E4_13_2.htm5/21/2018 19 PROGRAMACION LENGUAJE C++
7/34
3 Operador subndice
Para mimetizar este comportamiento con los objetos de la nueva clase empezaremos por poder
referenciarlos mediante el operador subndice[ ](L.1 ). Este operador debe recibir unintydevolver el miembro correspondiente de la matriz (recordemos que los miembros son
tipoVector). Como sabemos que debe gozar del doble carcter de Rvalue y Lvalue (L.3 ),
deducimos que debe devolver una referencia ( 4.9.18c). A "vote pronto" podra parecernos quela definicin debe ser del tipo:
const size_t sV = sizeof(Vector);Vector& operator[](int i) { return *( mVptr + (i * sV)); }
sin embargo, reflexionando ms detenidamente recordamos quemVptrest definidoprecisamente como puntero-a-Vector, por lo que su lgebra lleva implcito el tamao de los
objetosVector( 4.2.2), lo que significa que podemos prescindir del factorsV:
Vector& operator[](int i) { return *( mVptr + i ); }
Recordando la definicin de subndice 1a , y la relacin entre punteros y matrices ( 4.3.2)laexpresin anterior equivale a:
Vector& operator[](int i) { return mVptr[i]; }
esta es justamente la definicin que utilizamos para la funcin-operadoroperator[ ]( L.30) denuestra clase. Como resultado, podemos utilizar expresiones del tipo:
mVector mV1(5); // objeto tipo mVector (matriz de 5 Vectores)mV1[2]; // tercer elemento de la matriz
4 Operador de asignacin
Para utilizar el operador de asignacin=con los objetos devueltos por el selector de miembro[ ],debemos sobrecargarlo para los objetos tipoVector. Esto se ha visto en el epgrafe
correspondiente, por lo que nos limitamos a copiar dicha definicin ( 4.9.18a):
Vector& operator= (const Vector& v) { // funcin operator=x = v.x; y = v.y;return *this;
}
Su implementacin en la versin definitiva ( L.6), nos permite utilizar expresiones del tipo:
Vector v1;v1 = mV1[0]; // M.6:
5 Producto por un escalar
http://www.zator.com/Cpp/E4_9_18c.htm#Sobrecarga%20Op%20preincrementohttp://www.zator.com/Cpp/E4_9_18c.htm#Sobrecarga%20Op%20preincrementohttp://www.zator.com/Cpp/E4_9_18c.htm#Sobrecarga%20Op%20preincrementohttp://www.zator.com/Cpp/E4_2_2.htm#Ref-1http://www.zator.com/Cpp/E4_2_2.htm#Ref-1http://www.zator.com/Cpp/E4_2_2.htm#Ref-1http://www.zator.com/Cpp/E4_3_2.htm#Ref-1http://www.zator.com/Cpp/E4_3_2.htm#Ref-1http://www.zator.com/Cpp/E4_3_2.htm#Ref-1http://www.zator.com/Cpp/E4_9_18d.htm#Ref-4http://www.zator.com/Cpp/E4_9_18d.htm#Ref-4http://www.zator.com/Cpp/E4_9_18a.htm#Ejemplo-4http://www.zator.com/Cpp/E4_9_18a.htm#Ejemplo-4http://www.zator.com/Cpp/E4_9_18a.htm#Ejemplo-4http://www.zator.com/Cpp/E4_9_18d.htm#Ref-4http://www.zator.com/Cpp/E4_9_18d.htm#Ref-4http://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htm#Ref-4http://www.zator.com/Cpp/E4_9_18a.htm#Ejemplo-4http://www.zator.com/Cpp/E4_9_18d.htm#Ref-4http://www.zator.com/Cpp/E4_3_2.htm#Ref-1http://www.zator.com/Cpp/E4_2_2.htm#Ref-1http://www.zator.com/Cpp/E4_9_18c.htm#Sobrecarga%20Op%20preincremento5/21/2018 19 PROGRAMACION LENGUAJE C++
8/34
Para mimetizar el comportamiento expresado en L.4 , sobrecargamos el operador producto para
la claseVectorde forma que acepte unint. La definicin la hacemos de forma que corresponda ala definicin tradicional. Es decir, la resultante es un vector cuyos componentes son el producto de
los componentesx ydel vector operando por el escalar.
Es importante observar aqu que, en el caso de la matriz de enterosm, las dos sentencias
siguientes son equivalentes:
m[i] = m[j] * 3; // producto por un escalar (por la derecha)m[i] = 3 * m[j]; // producto por un escalar (por la izquierda)
5.1 Esto significa que debemos definir el producto en ambos sentidos. Para el primero podemos
definir una funcin miembro que acepte un argumento tipoint(adems del correspondientepunterothis). Este mtodo tiene el aspecto que se indica:
Vector operator* (int i) { // producto por un escalar (por la derecha)Vector vr;vr.x = x * i;vr.y = y * i;return vr;
}
Despus de implementado en la versin definitiva ( L.9), nos permite expresiones del tipo:
mV1[4] = mV1[0] * 5; // M.8:
5.2 El producto por la izquierda debemos definirlo como una funcin-operador externa. Se trata
de una funcin independiente (no pertenece a una clase) que acepta dos argumentos, un inty
unVector. Como es usual, la declaramosfriendde la claseVector( L.15) para que puedatener acceso a sus miembros (aunque en este caso no es necesario porque todos son pblicos).
Su diseo es muy parecido al anterior, aunque en este caso no existe puntero implcito thisydebemos referenciar el objetoVectordirectamente:
Vector operator* (int i, Vector v) {Vector vr;vr.x = v.x * i;vr.y = v.y * i;return vr;
}
Su implementacin ( L.38) hace posible expresiones como:
mV1[2] = 5 * mV1[0]; // M.11:
6 Una vezintroducidas todas las modificaciones anteriores en la versin bsica , el diseoresultante es el siguiente:
http://www.zator.com/Cpp/E4_9_18d.htm#Ref-4http://www.zator.com/Cpp/E4_9_18d.htm#Ref-4http://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htmhttp://www.zator.com/Cpp/E4_9_18d.htm#Ref-45/21/2018 19 PROGRAMACION LENGUAJE C++
9/34
#include using namespace std;
class Vector { // definicin de clase Vectorpublic: int x, y;Vector& operator= (const Vector& v) { // L.6: asignacin V = Vx = v.x; y = v.y; return *this;
}Vector operator* (int i) { // L.9: Producto V * intVector vr;vr.x = x * i; vr.y = y * i;return vr;
}void showV();friend Vector operator* (int, Vector); // L.15: Producto int * V
};void Vector::showV() { cout
5/21/2018 19 PROGRAMACION LENGUAJE C++
10/34
Salida:
X = 2; Y = 3X = 2; Y = 3X = 10; Y = 15X = 10; Y = 15
Comentario
En un programa real, se debera implementar un mecanismo de control de excepciones que
pudiera controlar la posibilidad de que el operadornewdel constructor (L.25) fuese incapaz de
crear el objeto ( 4.9.20). Es decir, controlar que operaciones como la de M.1 concluyen conxito.
Para manejar convenientemente los lmites incluimos enmVectorel miembrodimension(L.20);su valor es iniciado por el constructor (L.24 ) y acompaa a cada instancia. El efecto es que esposible implementar la interfaz de la clase de forma que el usuario no pueda acceder un elementofuera del espacio de la matriz.
Para facilitar la lectura incluimos en lamVectorel mtodoshowmem(L.31) que muestra los
componentes de un elemento de la matriz. Este mtodo utiliza el miembrodimensionparaverificar que no pretendemos acceder a un elemento fuera de los lmites del objeto previamentecreado.
4.9.18e Sobr ecarga del operado r ->
1 Antecedentes
Elselector indirecto de miembro->( 4.9.16)es un operador binario [1]que permite acceder amiembros de objetos cuando se dispone de punteros a la clase correspondiente. Una expresin deltipoCpt->membrrepresenta el miembro de identificadormembrde la claseClsiemprequeCptsea un puntero a dicha clase. Ejemplo:
class Cl {public: int x;
} c1, *ClPt = &c1;...ClPt->x = 10;
Sabemos que la expresinClPt->x exige que el primer operandoClptsea un puntero a la
clase, y que el segundox, sea el identificador de uno de sus miembros.
2 Sinopsis
La gramtica C++ permite definir una funcin miembrooperator->que puede ser invocada con lasintaxis del operador selector indirecto de miembro->. Por ejemplo, siendoobjuna instancia de
http://www.zator.com/Cpp/E4_9_20.htm#Controlar%20la%20operaci%C3%B3nhttp://www.zator.com/Cpp/E4_9_20.htm#Controlar%20la%20operaci%C3%B3nhttp://www.zator.com/Cpp/E4_9_20.htm#Controlar%20la%20operaci%C3%B3nhttp://www.zator.com/Cpp/E4_9_16.htm#Selector%20indirecto%20de%20miembrohttp://www.zator.com/Cpp/E4_9_16.htm#Selector%20indirecto%20de%20miembrohttp://www.zator.com/Cpp/E4_9_16.htm#Selector%20indirecto%20de%20miembrohttp://www.zator.com/Cpp/E4_9_18e.htm#[1]http://www.zator.com/Cpp/E4_9_18e.htm#[1]http://www.zator.com/Cpp/E4_9_18e.htm#[1]http://www.zator.com/Cpp/E4_9_18e.htm#[1]http://www.zator.com/Cpp/E4_9_16.htm#Selector%20indirecto%20de%20miembrohttp://www.zator.com/Cpp/E4_9_20.htm#Controlar%20la%20operaci%C3%B3n5/21/2018 19 PROGRAMACION LENGUAJE C++
11/34
la claseClpara la que se define la funcinoperator->, ymembrun miembro de la misma [2], la
expresin:
obj->membr;
es transformada por el compilador en una invocacin del tipo:
( obj.operator->() )->membr;
La parte entre parntesisobj.operator->(), representa la invocacin del mtodooperator-
>sobre el objetoobj. Puesto que el valor devuelto por la funcin ser considerado a su vez el
primer operando de->aplicado amembr, la funcinoperator->debe devolver un puntero a unobjeto de la clase sobre el que se pueda aplicar el operador->. Es decir, su diseo debe tener elsiguiente aspecto:
class Vector {...Vector* operator-> () {...return this;
}};
Observe que el puntero que se obtiene como resultado de la invocacin obj.operator->()no
depende de la naturaleza del operandomembr. Por esta razn se considera a veces queoperator-
>es un operador unario posfijo ( 4.9). Lo que significa que una expresin como
v.operator->();
tiene sentido y devuelve un puntero al objeto:
Vector v1;Vector* vptr;vptr = v1.operator->(); // Ok! vptr seala ahora a v1
3 Condiciones
Para conseguir este comportamiento el compilador impone ciertas limitaciones, de forma que la
funcinoperator->solo puede ser sobrecargada cumpliendo simultneamente las siguientescondiciones:
a. Ser una funcin-miembro no esttica (que incluya el punterothiscomo argumento
implcito 4.11.6).
b. Ser una funcin-miembro que no acepte argumentos.
Ejemplo:
http://www.zator.com/Cpp/E4_9_18e.htm#[2]http://www.zator.com/Cpp/E4_9_18e.htm#[2]http://www.zator.com/Cpp/E4_9_18e.htm#[2]http://www.zator.com/Cpp/E4_9.htm#Clasificaci%C3%B3nhttp://www.zator.com/Cpp/E4_9.htm#Clasificaci%C3%B3nhttp://www.zator.com/Cpp/E4_9.htm#Clasificaci%C3%B3nhttp://www.zator.com/Cpp/E4_11_6.htmhttp://www.zator.com/Cpp/E4_11_6.htmhttp://www.zator.com/Cpp/E4_11_6.htmhttp://www.zator.com/Cpp/E4_9.htm#Clasificaci%C3%B3nhttp://www.zator.com/Cpp/E4_9_18e.htm#[2]5/21/2018 19 PROGRAMACION LENGUAJE C++
12/34
class Cl {...friend Cl* operator->(); // Error debe ser una funcin-miembroCl* operator->(int i) {/*...*/} // Error no acepta argumentosCl* operator->() {/*...*/} // Ok.
};
4 El operador ->no puede ser sobrecargado
Aunque esta afirmacin puede parecer escandalosa, ya que est en contradiccin con el ttulo delcaptulo. Y adems, en cualquier bibliografa que se consulte, la descripcin de la
funcinoperator->se encuentra siempre en el captulo dedicado a la sobrecarga de operadores[3]. Sin embargo, no se trata de una verdadera sobrecarga del selector indirecto ->. Al menos, no
en el sentido en que este mecanismo funciona con el resto de operadores.
Observe que en realidad, el compilador se limita a sustituir el primer operando de la
expresinobj->membrpor la invocacin a una funcin-miembro, y una posterior utilizacin del
resultado como primer operando de laversin globaldel operador, mientras el segundo operandose mantiene invariable. Adems, en dicha expresin (invocacin de la funcin-miembro), elprimer operando debe ser necesariamente un objeto (instancia) de la clase y no un
punteroCl*como exige el uso regular del selector->.
4.1 Este comportamiento, distinto de aquellos casos en que la versin global del operador es
sustituida "realmente" por la versin sobrecargada, puede verificarse con un sencilloejemplo:
#include using namespace std;
class Vector {public:int x, y;bool operator== (Vector v) { // L6: sobrecarga operador ==cout cout
5/21/2018 19 PROGRAMACION LENGUAJE C++
13/34
Invocada funcion operator==()Distintosv1.x == 2
Comentario
Como puede verse, la utilizacin del operador de identidad ==en M.4, provoca la utilizacin de la
versin sobrecargada ( 4.9.18b1). As mismo, la ausencia de la definicin de este operador(L.6), habra producido un error de compilacin al tratar de utilizarlo en M.4:
'operator==' not implemented in type 'Vector' for arguments of the sametype in ...
Esto significa lisa y llanamente que el compilador no proporciona una versin por defecto de este
operador (de identidad) para los objetos de la clase Vector.
En cambio, la utilizacin de la (supuesta) versin sobrecargada del selector indirecto de miembro-
>(M.5), no produce la invocacin automtica de la misma como ocurri en el caso de laidentidad. En realidad, ante expresiones del tipovptr->xcomo en M.5, el compilador sigueutilizando la versin global (por defecto) del operador.
4.2 Si enel ejemplo anterior sustituimos las lneas M.4/5 por:
cout . Observequea,bycson equivalentes, y representan variaciones sintcticas para referirse al
elementov1.x.
5 Punteros inteligentes
Debemos resaltar que en el programa anterior disponemos de dos formas de acceso indirecto a los
miembros del objetov1:
http://www.zator.com/Cpp/E4_9_18b1.htmhttp://www.zator.com/Cpp/E4_9_18b1.htmhttp://www.zator.com/Cpp/E4_9_18b1.htmhttp://www.zator.com/Cpp/E4_9_18b1.htm5/21/2018 19 PROGRAMACION LENGUAJE C++
14/34
cout x; //acout x; //d
Hemos indicado que ambas utilizan la versin global del selector->sobre el miembroxcomosegundo operando. Pero existe una diferencia crucial: la forma apermite introducir una funcinprevia, representada poroperator->( ), lo que abre todo un mundo de posibilidades.
En realidad, este comportamiento atpico de la funcinoperator->( ), que hemos visto se apartadel resto de operadores, no es arbitraria. Representa la puerta de acceso a lo que se
denominanpunteros inteligentes; objetos que actan como punteros, pero que adems puedenrealizar alguna accin previa cada vez que un objeto es accedido a travs de ellos. Habida cuentaque esta accin previa puede ser cualquiera (todo lo que pueda hacer una funcin), los punterosinteligentes permiten tcnicas de programacin muy interesantes.
La idea puede ser concretada en tres formas bsicas que comentamos separadamente:
1. Incluir la funcinoperator->en la definicin de la clase2. Incluir la funcinoperator->en una clase independiente3. Incluir la funcinoprator->en una clase anidada
5.1 Incluir la funcinoperator->( ) en la definicin de la clase (es el caso del ejemplo anterior):
class Vector {public: int x, y;Vector* operator-> () {/* funcionalidad adicional requerida */return this;
}
};
Como se ha visto, este diseo permite que los objetos de la clase Vectorpuedan ser accedidosindirectamente:
Vector v1;v1->x = 2; v1->y = 4;
5.2 Incluir la funcinoperator->( ) en una claseVptrindependiente:
class Vector {
...};
class Vptr {...Vector* vpt;Vector* operator->() {/* funcionalidad adicional requerida */return vpt;
}
5/21/2018 19 PROGRAMACION LENGUAJE C++
15/34
};
En este casolos objetosVptrpueden ser utilizados para acceder a los de claseVector, de formaparecida a como se utilizan los punteros. Lo ilustramos con un ejemplo compilable:
#include using namespace std;
class Vector { public: int x, y; };
class Vptr {public:class Vector* vpt;Vector* operator->() {cout
5/21/2018 19 PROGRAMACION LENGUAJE C++
16/34
pueden ser utilizados para otras clases, esta disposicin tambin puede ser vlida y en ciertaforma equivalente al diseo anterior.
Veamos este diseo en un ejemplo concreto:
#include
using namespace std;
class Vector {public: int x, y;class Vptr {public:Vector* vpt;Vector* operator-> () {
cout
5/21/2018 19 PROGRAMACION LENGUAJE C++
17/34
Vector* operator-> () {cout
5/21/2018 19 PROGRAMACION LENGUAJE C++
18/34
return s;}...float (*fptr)(int, int) = sum; // definicin de puntero-a-funcinfloat (&fref)(int, int) = sum; // definicin de referencia-a-funcinint x = 2, y = 5;sum(x*2, y); // Ok. invocacinfptr(x*2, y); // Ok. invocacinfref(x*2, y); // Ok. invocacin
1.2 Cuando se utiliza con funciones-miembro (mtodos),expresin-postfijaes el nombrede un mtodo, una expresin de puntero-a-clase (utilizado para seleccionar un mtodo), o depuntero-a-miembro. Por ejemplo:
class Vector { // una clase cualquierafloat x, y;public: void getm(int i) { // funcin-miembro (mtodo)cout
5/21/2018 19 PROGRAMACION LENGUAJE C++
19/34
siendoobjunainstanciade la claseClpara la que se define la funcinoperator( ). Por ejemplo:
class Cl {public:...void operator()(int x) { /* definicin */ }
...};
...Cl obj;obj(5); // Ok!!
Cuando se utiliza esta notacin, el compilador transforma la expresin anterior en una invocacin
aoperator( )en la forma cannica:
obj.operator()( ); //2b
Observe que se trata simplemente de la invocacin de una funcin-miembro sobre el objetoobj, yque nada impide que sea invocada directamente al modo tradicional con la sintaxis cannica2b . Es decir, utilizando explcitamente la sustitucin realizada por el compilador.
No confundir la expresin anterior (2a ) con la utilizacin deoperator( )como operador de
conversin ( 4.9.18k), donde se utiliza sin especificacin del valor devuelto y sin que puedaaceptar ningn tipo de parmetro:
operator( ){ /* valor devuelto */ } //2c
Recordemos queoperator( )puede aparecer con dos significados en el interior de una clase:
class C {valor-devuelto operator()(argumentos); // operador de invocacin a
funcinoperator() { /* ... */ } // operador de conversin...
};
2.1 Lo ilustramos con unejemplo:
#include using namespace std;
class Vector {public:float x, y;void operator()() { // funcin-operadorcout
5/21/2018 19 PROGRAMACION LENGUAJE C++
20/34
Vector v1 = {2, 3};v1(); // Ok. invocacin de v1.operator()v1.operator()(); // Ok. invocacin clsica
}
Salida
Vector: (2, 3)Vector: (2, 3)
3 El operador de invocacin de funcin no es sobrecargable
Respecto a la "sobrecarga" del operador de invocacin de funcin ( ), podemos decir algo anlogo
a lo indicado para la sobrecarga del selector indirecto ->; ( 4.9.18e): a pesar de que en laliteratura sobre el tema, la descripcin de la funcinoperator( )se encuentra siempre en elcaptulo dedicado a la sobrecarga de operadores, en realidad no se trata de tal sobrecarga. Almenos no en un sentido homogneo al empleado con el resto de opradores. Hemos visto que se
trata de una mera curiosidad sintctica; una forma algo extraa de invocacin de determinadasfunciones-miembro (cuando estas funciones responden a un nombre especial).
Al hilo de lo anterior, y dado que el identificadoroperator( )es nico, resulta evidente que si se
definen varias de estas funciones, se aplicar la congruencia estndar de argumentos ( 4.4.1a)para resolver cualquier ambigedad. Por ejemplo:
#include using namespace std;
class Vector {public: float x, y;void operator()() { // L.6 Versin-1
cout
5/21/2018 19 PROGRAMACION LENGUAJE C++
21/34
Comentario
Observe que en L.9, el argumento ( int) de la segunda definicin se ha utilizado exclusivamentepara permitir al compilador distinguir entre ambas. Esta tcnica ya la hemos visto en la sobrecarga
de los post-operadores incremento y decremento ( 4.9.18c).
4 Objetos-funcin
Lo indicado hasta aqu podra parecer un mero capricho sintctico del creador del lenguaje; unaforma particular de invocacin de ciertas funciones-miembro (de nombre especial), que presentanla singularidad de permitir utilizar objetos como si fuesen funciones (caso de las expresiones b y
c ), pero que no tienen una justificacin objetiva, ya que no resuelve un problema que no puedaser resuelto con alguno de los recursos existentes en el lenguaje.
Precisamente, en razn de que pueden ser utilizadas como funciones, las instancias de clases
para las que se han definido funcionesoperator( ), reciben indistintamente el nombre deobjetos-funcin, funciones-objeto [2]o functor, y algn autor ha definido a estas entidades como "datos
ejecutables" [3].
En realidad, como ocurre con otros detalles de su diseo, este aparente capricho sintcticoencierra un mundo de sutilezas. Su importancia y razn de ser estriban en que permite escribir
cdigo que realiza operaciones complejas a travs de argumentos de funciones ( 5.1.3a1).
Precisamente la Librera Estndar de Plantillas C++ (STL 5.1)o sus extensiones, como laslibreras Boost, donde se encuentran algunos de los conceptos y algoritmos ms sofisticados quehaya construido hasta el momento la ingeniera de software, utiliza con profusin este tipo derecursos.
Recuerde que los objetos (instancias de clases) pueden ser pasados como argumentos defunciones y que sus mtodos pueden acceder a las propiedades de la clase, de forma que pasar
unobjeto-funcincomo argumento, equivale a pasar a la funcin ms informacin de la quesupondra un escalar. Esta circunstancia tiene muchas aplicaciones. Por ejemplo, la nueva versindel Estndar permitir crear en una aplicacin un hilo ("thread") de ejecucin mediante unaexpresin del tipo:
void work_to_do(); // L.1std::thread newThread (work_to_do); // L.2
La funcinwork_to_do()define el proceso que se ejecutar en el nuevo hilo representado por el
objetonewThreaddeL.2. Sin embargo, el constructor de la clasestd::trheadexige comoargumento un puntero a funcin que no recibe argumentos y devuelve void. De forma que no esposible pasar en el constructor ninguna informacin adicional sobre detalles de la tarea a realizar y
es en este punto, donde las caractersticas de C++ vienen al rescate, porque al igual que muchosotros algoritmos de la Librera Estndar C++, el argumento no tiene porqu ser necesariamente
una funcin ordinaria; tambin puede ser unobjeto-funcin, as que el diseo podra ser comosigue:
class Work_to_do {public:// miembros que representan particularidades del procesoint a_, b_;// miembro que representa el resultado del proceso
http://www.zator.com/Cpp/E4_9_18c.htm#Sobrecarga%20Op%20@++/@--http://www.zator.com/Cpp/E4_9_18c.htm#Sobrecarga%20Op%20@++/@--http://www.zator.com/Cpp/E4_9_18c.htm#Sobrecarga%20Op%20@++/@--http://www.zator.com/Cpp/E4_9_18f.htm#[2]http://www.zator.com/Cpp/E4_9_18f.htm#[2]http://www.zator.com/Cpp/E4_9_18f.htm#[2]http://www.zator.com/Cpp/E4_9_18f.htm#[3]http://www.zator.com/Cpp/E4_9_18f.htm#[3]http://www.zator.com/Cpp/E4_9_18f.htm#[3]http://www.zator.com/Cpp/E5_1_3a1.htmhttp://www.zator.com/Cpp/E5_1_3a1.htmhttp://www.zator.com/Cpp/E5_1_3a1.htmhttp://www.zator.com/Cpp/E5_1.htmhttp://www.zator.com/Cpp/E5_1.htmhttp://www.zator.com/Cpp/E5_1.htmhttp://www.zator.com/Cpp/E5_1_3a1.htmhttp://www.zator.com/Cpp/E4_9_18f.htm#[3]http://www.zator.com/Cpp/E4_9_18f.htm#[2]http://www.zator.com/Cpp/E4_9_18c.htm#Sobrecarga%20Op%20@++/@--5/21/2018 19 PROGRAMACION LENGUAJE C++
22/34
int& c_;// constructor que permite fijar las caractersticasWork_to_do (int a, int b, int& c) : a_(a), b_(b), c_(c) {}
// operador de invocacin a funcinvoid operator()() { c_ = a_ + b_; }
};...int r;std::thread newThread (Work_to_do(1,2,r));
Observe que el argumentoWork_to_do(x,y,z)es una llamada al constructor de la clase, que
genera unobjeto-funcin; que a su vez, es pasado al constructor de la clase std::trheadpara
construir el objetonewThread(que representa el nuevo hilo). Una vez concluida la tarea, el
resultado lo obtenemos enr.
4.1 Unin de argumentos
Observe que en el ejemplo anterior, el recurso ha consistido en empaquetar los argumentos
involucrados en un objeto-funcin. Esta tcnica, conocida como unin o empaquetado deargumentos("argument binding") es ampliamente utilizada, aunque tal como la hemospresentado, tiene el inconveniente de que hay que preparar manualmente la clase adecuada. Sinembargo, si como suele ser frecuente [4]la situacin se repite, es posible automatizarla utilizandouna plantilla.
Supongamos que el proceso que deba ejecutar la hebra ("thread") pueda ser definidagenricamente mediante una funcin del tipo
void work_to_do (A a, B b, C& c);
En la que los objetosaybrepresentan los datos particulares ycla variable en la que se obtiene
el resultado. En estas circunstancias, es posible definir una clase genrica ( 4.12.2)tal como:
template class bind {public:A a_;B b_;C& c_;void (*pf)(A,B,C&);bind (void (*p)(A,B,C&), A a, B b, C& c)
: pf(p), a_(a), b_(b), c_(c) {}
void operator()() { pf(a_, b_, c_); }};
Suponiendo que tenemos definida la funcin que realiza el proceso en un caso concreto:
void process1 (int a, char b, float& c) {/* proceso a realizar por la hebrael resultado es situado en c */...
}
http://www.zator.com/Cpp/E4_9_18f.htm#[4]http://www.zator.com/Cpp/E4_9_18f.htm#[4]http://www.zator.com/Cpp/E4_9_18f.htm#[4]http://www.zator.com/E4_12_2.htmhttp://www.zator.com/E4_12_2.htmhttp://www.zator.com/E4_12_2.htmhttp://www.zator.com/E4_12_2.htmhttp://www.zator.com/Cpp/E4_9_18f.htm#[4]5/21/2018 19 PROGRAMACION LENGUAJE C++
23/34
Para lanzar una hebra que realizara esa tarea, solo tendramos que incluir un par de lenas ennuestro cdigo:
float f;std:thread tread1 (bind (process1, 2, 'c', f));
4.9.18g Sobrec arga de operad ores lgico s
1 Sinopsis:
Recordemos ( 4.9.8)que los operadores lgicos suelen ser representados por sus nombres eningls (maysculas): AND (&&); OR (||) y NOT (!). Los dos primeros son binarios, mientras que elltimo es unario, lo que significa que AND y OR aceptan dos argumentos, mientras que la negacinNOT, acepta solo uno. Recordemos tambin que los operandos de las versiones globales de estos
operadores son convertidos a un tipobool, y que el resultado es tambin un tipoboolde valor
cierto/falso (true/false).
Cualquier intento de aplicar estos operadores a tipos abstractos ( 2.2)genera un error decompilacin recordndonos que la operacin solo est definida para los tipos bsicos(preconstruidos en el lenguaje). La solucin en estos casos es sobrecargar adecuadamente estosoperadores para los miembros de la clase, lo que puede realizarse mediante los procedimientos
estndar ya sealados para operadores unarios ( 4.9.18c)y binarios ( 4.9.18b).
2 Permanencia de las leyes formales
Antes de exponer algunos ejemplos, recordemos los preceptos que hemos denominado de
permanencia de las leyes formales ( 4.9.8)que son especialmente pertinentes en estos casosde sobrecarga.
Hemos sealado que la sobrecarga permite al programador una gran libertad, de forma que puedecambiar totalmente la funcionalidad del operador respecto a la que tiene con los tipos bsicos. Por
ejemplo, podemos definir el operador AND entre miembrosc1yc2de una claseCde forma quedevuelva un valor de cualquier tipo en vez de un booleano, aunque lo lgico sera que fuese
unbool, con objeto que el resultado de estas operaciones fuese el que se espera intuitivamente.Adems de esto, considere que los tres operadores estn relacionados desde el punto de vistalgico, y deberan seguir estndolo de la misma forma en las versiones sobrecargadas.
Por ejemplo, si definimos la sobrecarga del operador de negacin lgica NOT de forma que para
los objetosc1yc2resulta:
!c1 == false!c2 == false
Deberamos definir la sobrecarga del operador AND de forma que
(c1 && c2) == true
http://www.zator.com/Cpp/E4_9_8.htmhttp://www.zator.com/Cpp/E4_9_8.htmhttp://www.zator.com/Cpp/E4_9_8.htmhttp://www.zator.com/Cpp/E2_2.htm#&c%20Tipos%20complejos.http://www.zator.com/Cpp/E2_2.htm#&c%20Tipos%20complejos.http://www.zator.com/Cpp/E2_2.htm#&c%20Tipos%20complejos.http://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E4_9_18b.htmhttp://www.zator.com/Cpp/E4_9_18b.htmhttp://www.zator.com/Cpp/E4_9_18b.htmhttp://www.zator.com/Cpp/E4_9_18.htm#Permanencia%20leyes%20formaleshttp://www.zator.com/Cpp/E4_9_18.htm#Permanencia%20leyes%20formaleshttp://www.zator.com/Cpp/E4_9_18.htm#Permanencia%20leyes%20formaleshttp://www.zator.com/Cpp/E4_9_18.htm#Permanencia%20leyes%20formaleshttp://www.zator.com/Cpp/E4_9_18b.htmhttp://www.zator.com/Cpp/E4_9_18c.htmhttp://www.zator.com/Cpp/E2_2.htm#&c%20Tipos%20complejos.http://www.zator.com/Cpp/E4_9_8.htm5/21/2018 19 PROGRAMACION LENGUAJE C++
24/34
De lo contrario estaramos construyendo para los objetos de tipoCuna lgica bastante difcil decomprender a una mente acostumbrada al razonamiento estndar con los tipos bsicos dellenguaje.
En este captulo trataremos de demostrar que si se quiere mantener una lgica coherente, la
sobrecarga de los tres operadores lgicos AND, OR y NOT para tipos de una clase C, puede ser
sustituida por una conversin de usuario mediante una funcin de conversin operatorbool() adecuada ( 4.9.18k).
3 Sobrecarga del operador NOT
El operador NOT de negacin lgica (!) est relacionado con su contrario (que no tiene nombre nirepresentacin). Para explicar el significado de esta afirmacin, supongamos un objetocde unaclaseC. Segn hemos sealado, un intento de utilizarlo, por ejemplo, la expresin:
if (!c) { /* ... */ }
genera un error de compilacin:'operator!' not implemented in type 'C'..., en elque se indica que el operador NOT de negacin lgica no est definido para objetos de la clase.Sin embargo, podemos observar que un intento anlogo sin el operador:
if (c) { /* ... */ }
tambin produce un error, aunque en este caso la indicacin es ms ambiga: Illegalstructure operation in function...(Borland) o:conditional expression of type
'class C' is illegal(Visual C++). En este ltimo caso el compilador est indicando que no
sabe como convertir la expresin entre parntesis(c)a un tipobool. Recuerde que la
sentenciaif()...espera recibir una expresinque se resuelva en
unbool( 4.10.2).
3.1 Ambos inconvenientespueden resolverse adoptando las medidas pertinentes. El primerosobrecargando el operador NOT para objetos de la clase. El segundo proporcionando una
conversin de usuario que permita al compilador transformar un tipoCen unbool( 4.9.18k).Por ejemplo, supongamos una claseV2Dpara contener puntos de un plano definidos por suscoordenadas cartesianas. Para ciertas operaciones lgicas con estos objetos, consideramos"ciertos" los puntos que pertenecen al primer cuadrante y "falsos" todos los dems. Un posiblediseo sera el siguiente:
class V2D { // clase de puntos en el planofloat x, y;public:V2D(float i=0, float j=0): x(i), y(j) {} // constructorbool operator!() { // sobrecarga del operador NOTreturn ((x > 0 && y > 0) ? false : true );
}operator bool() { // conversin de usuarioreturn ((x > 0 && y > 0) ? true : false );
}
http://www.zator.com/Cpp/E4_9_18k.htm#Operadores%20de%20conversi%C3%B3nhttp://www.zator.com/Cpp/E4_9_18k.htm#Operadores%20de%20conversi%C3%B3nhttp://www.zator.com/Cpp/E4_9_18k.htm#Operadores%20de%20conversi%C3%B3nhttp://www.zator.com/Cpp/E4_10_2.htm#if%20elsehttp://www.zator.com/Cpp/E4_10_2.htm#if%20elsehttp://www.zator.com/Cpp/E4_10_2.htm#if%20elsehttp://www.zator.com/Cpp/E4_9_18k.htmhttp://www.zator.com/Cpp/E4_9_18k.htmhttp://www.zator.com/Cpp/E4_9_18k.htmhttp://www.zator.com/Cpp/E4_9_18k.htmhttp://www.zator.com/Cpp/E4_10_2.htm#if%20elsehttp://www.zator.com/Cpp/E4_9_18k.htm#Operadores%20de%20conversi%C3%B3n5/21/2018 19 PROGRAMACION LENGUAJE C++
25/34
};...void func () {V2D p1(0,2);V2D p2(-1.1, 2);V2D p3 = Vector2D(1,2);V2D p4(1, -3);
if (p1) cout
5/21/2018 19 PROGRAMACION LENGUAJE C++
26/34
V2D p1(0,2);V2D p2(-1.1, 2);V2D p3 = Vector2D(1,2);V2D p4(1, -3);
if (!!p1) cout
5/21/2018 19 PROGRAMACION LENGUAJE C++
27/34
La sobrecarga de AND se realiza mediante el procedimientoa(una funcin miembro no esttica);para OR se utiliza el procedimientob(una funcin externa que acepte dos argumentos). El diseoes el siguiente:
#include using namespace std;
class V2D {public:float x, y;V2D(float i=0, float j=0): x(i), y(j) { } // constructorbooloperator&&(V2D& p) { // funcin-operadorif (x > 0 && y > 0) {
if (p.x > 0 && p.y > 0) return true;}return false;
}friend bool operator||(V2D&, V2D&);
};
booloperator||(V2D& p1, V2D& p2) { // funcin-operadorif (p1.x > 0 && p1.y > 0) return true;if (p2.x > 0 && p2.y > 0) return true;return false;
}
int main() { // =================V2D p1(0,2);V2D p2(-1.1, 2);V2D p3 = V2D(1,2);V2D p4(1, -3);
if (p3 && p1) cout
5/21/2018 19 PROGRAMACION LENGUAJE C++
28/34
4.9.18h Sobrec arga de Enum eracion es
1 Sinopsis
El hecho de referirnos a la sobrecarga deenumeraciones( 4.7)podra parecer en
contradiccin con lo indicado al tratar de la sobrecarga en general ( 4.9.18): que "se refiere ytiene aplicacin solo cuando los operandos son instancias de clases". Sin embargo, hay que teneren cuenta que las enumeraciones son en realidad un tipo muy particular de estructuras (y porende, de clases), que como tales, gozan de muchas de las caractersticas de aquellas [1].
2 Sobrecarga
Es posible sobrecargar la mayora de los operadores para una enumeracin, pero dado que estasno pueden tener funciones-miembro, no es posible sobrecargar aquellos que precisamente exigen
ser sobrecargados a travs de mtodos no estticos. Concretamente los operadores=, [ ], ( )y ->nopueden ser sobrecargados para unenum.
La consecuencia inmediata es aadir que la sobrecarga de enumeraciones debe realizarse atravs de funciones ordinarias (no pertenecientes a clases), y que al menos uno de los argumentosdebe ser del tipo de la enumeracin que se sobrecarga.
3 Comprobar en el ejemplo que sigue la forma de sobrecargar los operadores de preincremento ypostincremento. Observe que en cierta forma, la enumeracin se comporta como una clase.
#include #using amespace std;
enum ESTACION { primavera, verano, otono, invierno };ESTACION& operator++ (ESTACION& s) { // Preincremento ++@
s = ESTACION( (s + 1) % 4 ); // L.6return s;
}ESTACION operator++(ESTACION& s, int) { // Postincremento @++
ESTACION tmp = s;switch (s) {
case primavera: s = verano; break;case verano: s = otono; break;case otono: s = invierno; break;case invierno: s = primavera; break;
}return tmp;
}
int main(void) { // =============ESTACION est = otono;cout
5/21/2018 19 PROGRAMACION LENGUAJE C++
29/34
Salida:
La estacion es 2Preincrementar la estacion: 3No cambiar si se usa el postincremento: 3Finalmente: 0
Comentario
Por tratarse de la sobrecarga de operadores unarios como funciones externas, ambas funciones-
operador se han definido utilizando la forma que hemos denominado "b" ( 4.9.18c):
Preincremento: funcin que acepta un argumento operator++(ESTACION)
Posincremento: funcin acepta un objeto y un entero operator++(ESTACION, int)
El preincremento recibe el argumento por referencia (L.5), de forma que modifica el valor recibido.
Observe como en L.6 se efecta el calculo aritmtico, seguido de un modelado al tipoESTACION, y
como en L.7 este valor es devuelto por referencia.
Observe como el postincremento devuelve un objeto "por valor", y que este objeto es una copia delobjeto inicial. Simultneamente la funcin modifica el valor del objeto recibido inicialmente (porreferencia).
4.9.18k Conversion es defin idas por el usu ario
El presente captulo es un claro ejemplo de un tpico difcil de clasificar.Aunque podra haber encajado igualmente bien (o mal) en otros sitios, lohemos incluido en el epgrafe dedicado a la sobrecarga de operadores porque
en una de sus formas se refiere a la funcin-operador operator. Este es
tambin el criterio del Dr. Stroustrup en su obra TC++PL.
1 Prembulo
Recordemos que el lenguaje C++ dispone de una serie de mecanismos de conversin para lostipos bsicos, que son utilizados automticamente en determinadas circunstancias. Son las
conversiones estndar ( 2.2.5). El lenguaje tambin permite que puedan realizarse conversionesimplcitas o explcitas para los tipos abstractos, aunque en este caso es el programador el quedebe adoptar las medidas pertinentes, razn por la cual se denominan conversiones definidaspor el usuario("User-defined conversions").
Existen dos formas de definir estas ltimas conversiones: mediante constructores y mediante
operadores de conversin. Ambos tipos sern tratados en el presente captulo.
2 Conversiones de constructor
Hemos indicado ( 4.9.9)que el modelado de tipos est estrechamente relacionado con losconstructores de clases, y que la posibilidad de realizar un modelado de un objeto bde tipo B, aotro tipo Adistinto:
http://www.zator.com/Cpp/E4_9_18c.htm#Formas%20b%C3%A1sicashttp://www.zator.com/Cpp/E4_9_18c.htm#Formas%20b%C3%A1sicashttp://www.zator.com/Cpp/E4_9_18c.htm#Formas%20b%C3%A1sicashttp://www.zator.com/Cpp/E2_2_5.htmhttp://www.zator.com/Cpp/E2_2_5.htmhttp://www.zator.com/Cpp/E2_2_5.htmhttp://www.zator.com/Cpp/E4_9_9.htm#Estilo%20cl%C3%A1sicohttp://www.zator.com/Cpp/E4_9_9.htm#Estilo%20cl%C3%A1sicohttp://www.zator.com/Cpp/E4_9_9.htm#Estilo%20cl%C3%A1sicohttp://www.zator.com/Cpp/E4_9_9.htm#Estilo%20cl%C3%A1sicohttp://www.zator.com/Cpp/E2_2_5.htmhttp://www.zator.com/Cpp/E4_9_18c.htm#Formas%20b%C3%A1sicas5/21/2018 19 PROGRAMACION LENGUAJE C++
30/34
a = A(b);
depende de cmo est definido el constructor de la clase A. Para que esta conversin sea posible,
debe existir un constructor de conversin ( 4.11.2d1)que acepte un objeto tipo Bcomo nicoargumento. Es decir, debe existir un mtodo:
A::A(B b) { /* detalle de la conversin */ }
En realidad, los constructores de conversin constituyen el soporte del mecanismo C++ demodelado, de forma que la existencia de estos constructores es condicin necesaria y suficientepara que pueda efectuarse este ltimo (el modelado). Por ejemplo:
class X {public:X(int); // constructor C-1
};
la mera existencia del constructor C-1 en la clase X, permite las siguientes asignaciones:
void f() {X a = 1; // Ok. invocacin implcita a X(1)X b(1); // Ok. invocacin implcita a X(1)a = 2; // Ok. invocacin implcita a X(2)a = (X) 2; // Ok. casting explcito (estlo tradicional)a = static_cast(2); // Ok. casting explcito (estilo C++)
}
Si eliminamos el constructor C-1 de la declaracin de la clase, las sentencias anteriores seranerrneas.
Nota: las tres ltimas sentencias implican en realidad dos operaciones: la creacin de unobjeto temporal tipoXconteniendo el Rvalue de la expresin y una asignacin posteriorutilizando el operador de asignacin implcito de la clase X.
2.1 Ejemplo
Para ilustrar la problemtica de este tipo de conversiones explcitas e implcitas, construiremos unejemplo ejecutable en el que creamos una claseParque destinaremos a albergar los enterosmltiplos de 2. Lo que pretendemos es poder utilizar los miembros de esta clase en todas las
circunstancias en que se podra utilizar un tipo bsico intsuponiendo que su valor sea un nmeropar (divisible por 2).
El criterio para aceptar que un entero npuede ser miembro de la clase es que el resto de ladivisin n/2 sea cero (consideramos que el cero es par, puesto que 0/2 es 0).
El diseo bsico es el siguiente:
#include using namespace std;
class BadNumber {
http://www.zator.com/Cpp/E4_11_2d1.htm#Constructores%20de%20conversi%C3%B3nhttp://www.zator.com/Cpp/E4_11_2d1.htm#Constructores%20de%20conversi%C3%B3nhttp://www.zator.com/Cpp/E4_11_2d1.htm#Constructores%20de%20conversi%C3%B3nhttp://www.zator.com/Cpp/E4_11_2d1.htm#Constructores%20de%20conversi%C3%B3n5/21/2018 19 PROGRAMACION LENGUAJE C++
31/34
int num; // miembro -privado por defecto-public:BadNumber(int n=0): num(n) {} // constructor explcitoint what() { return num; } // mtodo
};
class Par {int val;void verify(int n) { if (n % 2) throw BadNumber(n); }public:Par(int n=0) { // L.15: constructor de conversinverify(n);val = n;cout
5/21/2018 19 PROGRAMACION LENGUAJE C++
32/34
Se ha dotado a la clase de un constructor por defecto que acepta un intcomo argumento. Si elvalor nsuministrado pasa la verificacin correspondiente, se inicia el miembro valcon el valor delargumento ny se muestra en pantalla el nmero creado.
En los comentarios de las sentencias M2 a M6 se muestra la salida obtenida en cada caso. Puedecomprobarse que la existencia de un constructor de conversin como el definido en L.15 permite
los modelados implcitos en las sentencias M3 y M4 o explcitos (sentencias M5 y M6), donde lasconstantes numricas utilizadas como Rvalues son convertidas a objetos de tipo Par.
Observe que no ha sido necesario definir el operador de asignacin =entre objetos Par. La versin
por defecto suministrada por el compilador ( 4.9.18a)resulta suficiente para las asignacionesplanteadas.
2.1 Aumentar la funcionalidad
La clase diseada cumple con su funcin de almacenar nmero pares. Tambin permite una
asignacin de tipo Par= int, y detecta cualquier intento de crear un objeto no vlido. Sin embargo,
dista mucho de poder ser utilizada con la misma generalidad que los objetos de tipo int. Porejemplo, las siguientes sentencias produciran un error de compilacin:
Par p3 = p2 + 2; // ErrorPar p4 = p2 + p2; // ErrorPar p8 = 4 + p4; // Error
La razn es que no estn definidas las operaciones correspondientes:
suma Par& int; suma Par& Par, y suma int& Par[1].
Para acercar nuestro diseo a la funcionalidad deseada agregamos los operadorescorrespondientes. Seran los siguientes algoritmos:
Nota: en los ejemplos que siguen, pies un objeto tipo Par, y nes un int.
2.1a Operador suma +entre tipos Pare int(resuelve situaciones del tipo pi + n):
operator+(int n) {verify(n); // verificar que el operando nes adecuadoval += n;
}
2.1b Operador suma +para tipos Par (resuelve situaciones del tipo pi + pj):
Par operator+(const Par& p) {return Par(val + p.val);
}
Observe que el valor a devolver por la funcin se ha obtenido mediante una explcita al constructorutilizando el argumento adecuado. Aunque esta operacin no precisa de verificacin, de todasformas el constructor la realiza.
http://www.zator.com/Cpp/E4_9_18a.htmhttp://www.zator.com/Cpp/E4_9_18a.htmhttp://www.zator.com/Cpp/E4_9_18a.htmhttp://www.zator.com/Cpp/E4_9_18k.htm#[1]http://www.zator.com/Cpp/E4_9_18k.htm#[1]http://www.zator.com/Cpp/E4_9_18k.htm#[1]http://www.zator.com/Cpp/E4_9_18k.htm#[1]http://www.zator.com/Cpp/E4_9_18a.htm5/21/2018 19 PROGRAMACION LENGUAJE C++
33/34
2.1c Operador suma +entre tipos inty Par (resuelve situaciones del tipo n + pi):
Par operator+(int n, Par p) {Par pt(n); // L1:return pt + p; // L2:
}
La sentencia L1 crea un objeto automtico pttipo Pary valor n, mediante una invocacin implcitaal constructor de la clase (el constructor se encarga de verificar que el valor nes correcto). En L2
se utiliza el operador suma entre objetos tipo Par, definido en el punto anterior (2.1b ), paradevolver el objeto resultante de la operacin.
Una definicin alternativa y ms eficiente, sera la siguiente:
Par operator+(int n, Par p) {return Par(p.val + n);
}
Las dos primeras funciones-operador se integran como mtodos no estticos de clase; la ltimacomo funcin externa. En el listado adjunto se muestra el diseo resultante despus de las
adiciones anteriores ( Listado-1). El nuevo diseo permite realizar las operaciones deseadas
(2.1b ) con los resultados que se indican:
Par p3 = p2 + 1; // -> Error: numero 1 impar.Par p4 = p2 + p2; // -> Creado numero 4Par p5 = 3 + p2; // -> Error: numero 3 impar.Par p8 = 4 + p4; // -> Creado numero 4
// -> Creado numero 8
Observe que los resultados son los esperados; la doble salida generada por la ltima sentencia es
producida por el algoritmo 2.1c . La primera corresponde a la creacin del objeto
automtico pt. La segunda a la construccin del objeto a devolver realizada en L2.
2.2 Nuevas dificultades
No obstante lo anterior, una total libertad para la utilizacin conjunta de nuestro tipo Parcon el
tipo intexigira muchas ms posibilidades. Por ejemplo:
Par p2 += 2;Par p4 += p2;
Par p8 /= 2;++p2;
etc.
A las anteriores habra que aadir todas las circunstancias en que la conversin deba realizarse en
sentido contrario (del tipo Para int). Por ejemplo:
http://www.zator.com/Cpp/E4_9_18k.htm#Aumentar%20funcionalidadhttp://pup%28%27e4_9_18kw1.htm%27%29/http://pup%28%27e4_9_18kw1.htm%27%29/http://pup%28%27e4_9_18kw1.htm%27%29/http://www.zator.com/Cpp/E4_9_18k.htmhttp://www.zator.com/Cpp/E4_9_18k.htmhttp://www.zator.com/Cpp/E4_9_18k.htmhttp://www.zator.com/Cpp/E4_9_18k.htmhttp://www.zator.com/Cpp/E4_9_18k.htmhttp://www.zator.com/Cpp/E4_9_18k.htmhttp://www.zator.com/Cpp/E4_9_18k.htmhttp://www.zator.com/Cpp/E4_9_18k.htmhttp://www.zator.com/Cpp/E4_9_18k.htmhttp://www.zator.com/Cpp/E4_9_18k.htmhttp://www.zator.com/Cpp/E4_9_18k.htmhttp://www.zator.com/Cpp/E4_9_18k.htmhttp://pup%28%27e4_9_18kw1.htm%27%29/http://www.zator.com/Cpp/E4_9_18k.htm#Aumentar%20funcionalidad5/21/2018 19 PROGRAMACION LENGUAJE C++
34/34
int x = p2;int y = 3 + p2;y += p2;
etc.
Con el diseo actual de la clase Par, todas estas sentencias producen un error de compilacin. Suutilizacin exigira implementar toda una serie de versiones sobrecargadas de los operadorescorrespondientes, lo que supone desde luego una buena cantidad de cdigo.
Recommended