[C con Clase] estilo: Sobrecarga en funciones lectura escritura

Steven Davidson srd4121 en njit.edu
Lun Mar 9 00:19:09 CET 2009


Hola Zaka,

Zakariae El-Abdelouarti wrote:
> Hola Steven,
> 
> Tengo algunas preguntas sobre las respuestas a vicente lozano.
> 
> El día 6 de marzo de 2009 19:19, Steven Davidson <srd4121 en njit.edu>
> escribió:
>> int x() const  { return xv; }
>> int y() const  { return yv; }
>>
>> Con esto, indicamos que las funciones son constantes que significa que éstas
>> no tienen intención alguna de modificar el estado de este objeto.
> 
> ¿Por qué es necesario el operador const en estas declaraciones?¿De
> qué manera podrían las anteriores funciones modificar el estado del
> objeto?
> 

Desde nuestro punto de vista, sabemos que estas funciones miembros no 
modificarán el objeto, pero el compilador no lo sabe. Por ello, 
establecemos que estas funciones miembros no modificarán el objeto para 
que el compilador las tenga en cuenta. Esto implica que cuando manejemos 
objetos constantes, podamos invocar estas funciones constantes. Por ejemplo,

class punto
{
private:
   int xv;
   int yv;

public:
   punto( int vx=0, int vy=0 ) : xv(vx), yv(vy)  {}

   int x()  { return xv; }
   int y()  { return yv; }
   void x( int valor )  { xv=valor; }
   void y( int valor )  { yv=valor; }
};

ostream &operator<<( ostream &os, const punto &pt )
{
   return os << '(' << pt.x() << ',' << pt.y() << ')';
}

El compilador te marcará un error en la sobrecarga del operador << 
porque 'pt' es tratado como un objeto constante, pero estás usando 
funciones miembros que no son constantes. El compilador no puede 
asegurar la constancia del objeto 'pt' y por tanto da un error.

Si por el contrario, definimos la clase de esta manera, no tendremos tal 
problema:

class punto
{
private:
   int xv;
   int yv;

public:
   punto( int vx=0, int vy=0 ) : xv(vx), yv(vy)  {}

   int x() const  { return xv; }
   int y() const  { return yv; }
   void x( int valor )  { xv=valor; }
   void y( int valor )  { yv=valor; }
};

Ahora 'x()' e 'y()' son funciones constantes y por tanto pueden ser 
usadas con objetos constantes porque están aseguradas de no modificar el 
objeto.

>> p.x() = 5;
>> p.y() = 10;
> 
> Aquí supongo que estoy apelando a una de mis lagunas. Lo que hay a la
> izquierda de un operador de asignación de int -> int, ¿Es una
> variable?¿Es una dirección de memoria? ¿Puede ser cualquiera de las
> dos?
> 

En general, se suele decir que lo que hay a la izquierda de un operador 
de asignación es el llamado "lvalue" que en inglés se refiere a "left 
value". Este "valor-i" debe ser una variable. A bajo nivel, ciertamente 
la asignación usa la dirección de memoria de la variable. Por ejemplo,

int a;

a = 5;

Se traduciría en ensamblador como:

MOVE &a, 0x05

Por cierto, no estoy usando un lenguaje ensamblador específico; me lo he 
inventado. Esto sólo es un ejemplo, pero muestra la necesidad de saber, 
por parte del procesador, la dirección de memoria que será el destino de 
guardar el valor de 5.

Obviamente, lo siguiente no tiene sentido en C/C++:

int a;

5 = a;

Si quisiéramos escribir esto directamente en ensamblador, el resultado 
sería muy distinto:

MOVE 0x05, [a]

Posiblemente el procesador acabaría copiando el valor en 'a' en la 
dirección de memoria 0x05. Lo más seguro que el sistema operativo se 
verá forzado a terminar este programa de inmediato por haber violado el 
área de memoria de otro programa.


En fin, el ejemplo que di a Vicente implica que esas funciones miembros 
retornarían una referencia a la variable privada. Es decir,

p.x() = 5;

equivale al siguiente comportamiento:

p.xv = 5;


Espero haber aclarado las dudas.

Steven





Más información sobre la lista de distribución Cconclase