19 PROGRAMACION LENGUAJE C++

download 19 PROGRAMACION LENGUAJE C++

of 34

Transcript of 19 PROGRAMACION LENGUAJE C++

  • 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.htm
  • 5/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.htm
  • 5/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.htm
  • 5/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%20preincremento
  • 5/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-4
  • 5/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%B3n
  • 5/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.htm
  • 5/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.htm
  • 5/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%B3n
  • 5/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%A1sicas
  • 5/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%B3n
  • 5/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.htm
  • 5/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%20funcionalidad
  • 5/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.