1 Implementación de clases

Anuncio
1 Implementación de clases
La traducción a C++ de una clase sigue los mismos pasos que la implementación de un módulo
de datos, es decir, hay que obtener una representación de la misma a partir de las clases ya
existentes y, a continuación, codificar sus operaciones en C++.
Recordemos que para garantizar que la clase sea independiente de su implementación, el
acceso a los atributos de sus objetos ha de estar prohibido, salvo si se realiza mediante las operaciones de modificación y consulta (la mayoría de lenguajes proporciona mecanismos para eludir
esta prohibición; en otros contextos su uso puede ser ventajoso pero aquí no lo permitiremos).
Como ya hemos mencionado, necesitamos dos ficheros: el .hpp, que ha de contener al menos
las cabeceras de las operaciones y cierta información sobre la representación de la clase, y el
.cpp, que ha de incluir al menos el código de las operaciones.
En la implementación ya conocida de la clase Estudiante, el fichero Estudiante2.hpp
apenas hace mención de los campos de la clase y casi toda la información relevante está oculta
en el fichero Estudiante2.cpp. Esta técnica requiere conocimientos de C++ que sobrepasan
los objetivos de esta asignatura, pero es interesante haber visto un ejemplo de ella. En vuestras
clases no ocultaréis los campos pero al menos los declararéis privados en los ficheros .hpp, como
veremos a continuación.
En el ejemplo de la clase Estudiante, comenzamos por el fichero Estudiante.hpp. Las
cabeceras de las operaciones son las mismas que en la versión anterior y se declaran igualmente
públicas.
En cuanto a los campos, recordemos que había un campo entero para el DNI, otro real para
la nota y otro booleano para saber si la nota está definida. Todo ellos son declarados private
para obligar a que el acceso a los mismos desde fuera de la clase se realice mediante las operaciones correspondientes. Para limitar el conjunto de notas válidas, introducimos una constante
MAX_NOTA, que además será declarada static (ya que si no, cada objeto tendría su propia constante como atributo). Notad que no se ha de usar el equivalente en C++ al constructor de tipos
tupla, es decir, struct. Si necesitamos tuplas auxiliares (es decir, una tupla que forma parte de
otra tupla), entonces sí que emplearemos un struct.
Es interesante notar que las asignaciones entre miembros de una clase en realidad se descomponen simplemente en asignaciones campo a campo. En este caso, como los campos son de tipos
simples, las asignaciones de los estudiantes implementados no generan aliasing. Por lo tanto, ya
no necesitamos introducir el operador correspondiente,
#ifndef ESTUDIANTE_HPP
#define ESTUDIANTE_HPP
class Estudiante {
private:
int DNI;
double nota;
bool tieneNota;
1
static const int MAX_NOTA = 10;
public:
/* Constructoras */
Estudiante();
/* Destructora por defecto */
~Estudiante();
/* Consultoras de los campos */
bool tiene_nota() const;
double consultar_nota() const;
int consultar_DNI() const;
/* Modificadoras de los campos */
void crear_estudiante(int dni);
void anadir_nota(double nota);
void modificar_nota(double nota);
/* Entrada / Salida */
void leer_estudiante();
void escribir_estudiante() const;
};
#endif
Pasemos al fichero Estudiante.cpp, donde codificamos los métodos. Notad que en todas las cabeceras hay que insertar la declaración Estudiante:: delante del nombre de cada
operación. Así queda establecido que se trata de la misma operación que consta en el fichero Estudiante.hpp. En otros ejemplos más complejos, veremos que las operaciones que no
incluyan la mencionada declaración se consideran automáticamente como operaciones privadas.
En las instrucciones de un método será necesario referirse al parámetro implícito: para ello
C++ reserva la palabra this. Hay que notar que ésta no es siempre obligatoria: si mencionamos
el nombre de un campo de la clase, C++ interpreta que nos referimos al correspondiente campo del parámetro implícito. Sin embargo, en casos como los de las operaciones añadir_nota
y modificar_nota en los que existe la posibilidad de confusión entre el campo nota y el parámetro del mismo nombre, el uso del this es imprescindible. También lo es cuando se hace
referencia al parámetro implícito en su conjunto, por ejemplo, si se necesita pasarlo como parámetro no implícito de alguna operación.
Por último, veréis que algunos métodos lanzan excepciones para controlar situaciones no
previstas en la precondición, cada una con un texto explicativo (ER1,...,ER4, definidos en el
.cpp). En general, no pediremos que lo hagáis así en vuestros programas, sino que bastará con
comprobar que una precondición se cumple antes de usar la correspondiente operación.
2
Comenzamos con la creadora que genera un estudiante vacío y la destructora por defecto.
Las demás posibilidades quedan como ejercicio.
Estudiante::Estudiante(){}
Estudiante::~Estudiante(){}
Entre las modificadoras tenemos las operaciones crear_estudiante y las dos operaciones
que se encargan de la nota. Se verán otras opciones en los ejercicios.
void Estudiante::crear_estudiante(int dni)
{
if (dni<0) throw PRAPExcepcio(ER4);
DNI = dni;
tieneNota = false;
}
void Estudiante::anadir_nota(double nota)
{
if (tieneNota)
throw PRAPExcepcio(ER3);
if (nota < 0 or nota > MAX_NOTA)
throw PRAPExcepcio(ER2);
this->nota = nota;
tieneNota = true;
}
void Estudiante::modificar_nota(double nota)
{
if (not tieneNota)
throw PRAPExcepcio(ER1);
if (nota < 0 or nota > MAX_NOTA)
throw PRAPExcepcio(ER2);
this->nota = nota;
}
Pasemos a las operaciones consultoras. En este ejemplo, sólo tenemos las consultas a los
valores de los campos, pero en otras situaciones podríamos necesitar cálculos más complicados.
bool Estudiante::tiene_nota() const
{
return tieneNota;
}
3
double Estudiante::consultar_nota() const
{
if (!tieneNota) throw PRAPExcepcio(ER1);
return nota;
}
int Estudiante::consultar_DNI() const
{
return DNI;
}
Por último, las operaciones de lectura y escritura:
void Estudiante::leer_estudiante()
{
crear_estudiante(readint());
double x = readdouble();
if (x>=0 && x<=10)
anadir_nota(x);
}
void Estudiante::escribir_estudiante() const
{
if (tieneNota)
cout << DNI << " " << nota << endl;
else
cout << DNI <<" NP" << endl;
}
4
Descargar