Ejercicios de Clases
4. Funciones miembro constantes
Esta es una propiedad que nos será muy útil en la depuración de nuestras clases. Además proporciona ciertos mecanismos necesarios para mantener la protección de los datos.
Cuando una función miembro no modifique el valor de ningún dato de la clase, podemos y debemos declararla como constante. Esto no evitará que la función intente modificar los datos del objeto; a fin de cuentas, el código de la función lo escribimos nosotros; pero generará un error durante la compilación si la función intenta modificar alguno de los datos miembro del objeto.
Por ejemplo:
#include
using namespace std;
class Ejemplo2 {
public:
Ejemplo2(int a){A=a;};
void Modifica(int a) { A = a; }
//Método
int Lee() const { return A; }// Ver que método Lee es CONTS!!
//int Lee() const { A++; return A; }//intenta Modificar!!
//int Lee() const { Modifica(A+1); return A; }//intenta Modificar!!
//int Lee() const { Modifica(3); return A; }//intenta Modificar!!
private:
int A;
};
int main() {
Ejemplo2 X(6);
cout << X.Lee() << endl;
X.Modifica(2);
cout << X.Lee() << endl;
cin.get();
return 0;
}
Para experimentar, comprueba lo que pasa si cambias la definición de la función "Lee()" por estas otras, algunas de las comentadas en las líneas 12,13 o 14 :
int Lee() const { A++; return A; } int Lee() const { Modifica(A+1); return A; } int Lee() const { Modifica(3); return A; }
Verás que el compilador no lo permite.
Evidentemente, si somos nosotros los que escribimos el código de la función, sabemos si la función modifica o no los datos, de modo que en rigor no necesitamos saber si es o no constante, pero frecuentemente otros programadores pueden usar clases definidas por nosotros, o nosotros las definidas por otros. En ese caso es frecuente que sólo se disponga de la declaración de la clase, y el modificador "const" nos dice si cierto modifica o no los datos del objeto.
Valores de retorno constantes
Otra técnica muy útil y aconsejable en muchos casos es usar valores de retorno de las funciones constantes, en particular cuando se usen para devolver punteros miembro de la clase.
Por ejemplo, supongamos que tenemos una clase para cadenas de caracteres:
class cadena {
public:
cadena(); // Constructor por defecto
cadena(char *c); // Constructor desde cadena c
cadena(int n); // Constructor para cadena de n caracteres
cadena(const cadena &); // Constructor copia
~cadena(); // Destructor
void Asignar(char *dest);
char *Leer(char *c) {
strcpy(c, cad);
return c;
}
private:
char *cad; // Puntero a char: cadena de caracteres
};
Si te fijas en la función "Leer", verás que devuelve un puntero a la cadena que pasamos como parámetro, después de copiar el valor de cad en esa cadena. Esto es necesario para mantener la protección de cad, si nos limitáramos a devolver ese parámetro, el programa podría modificar la cadena almacenada a pesar de se cad un miembro privado:
char *Leer() { return cad; }
Para evitar eso podemos declarar el valor de retorno de la función "Leer" como constante:
const char *Leer() { return cad; }
De este modo, el programa que lea la cadena mediante esta función no podrá modificar ni el valor del puntero ni su contenido. Por ejemplo:
class cadena { ... }; ... int main() { cadena Cadena1("hola"); cout << Cadena1.Leer() << endl; // Legal Cadena1.Leer() = cadena2; // Ilegal Cadena1.Leer()[1] = 'O'; // Ilegal }