Escolar Documentos
Profissional Documentos
Cultura Documentos
Clases
Implementacin de una clase
Descripcin de una clase en C++ Elementos pblicos y privados Acceso directo a atributos Clases amigas Implementacin de las operaciones Funciones miembro inline Organizacin del cdigo en C++
Implementacin de clases
Partiendo del diseo orientado a objetos de un sistema, la tarea ms bsica a la que nos enfrentamos durante la fase de implementacin es la traslacin de cada clase al al lenguaje de programacin orientado a objetos que estemos utilizando.
Cuenta nmero: String titular: String saldo: Integer inters_anual: Real ingreso (cantidad: Integer) reintegro (cantidad: Integer) ingreso_interes () esta_en_rojos (): Boolean ver_saldo (): Integer
Ejemplo: un objeto que representa las cuentas en una aplicacin de gestin bancaria.
Fichero cuenta.hpp
class Cuenta { char numero[20]; char titular[80]; long int saldo; float interes; public:
void ingreso (long int cantidad); void reintegro (long int cantidad); void ingreso_interes (void); Operaciones int esta_en_rojos (void); (Funciones miembro) long int ver_saldo (void); };
void ingreso (long int cantidad); void reintegro (long int cantidad); void ingreso_interes (void); int esta_en_rojos (void); long int ver_saldo (void);
};
class Cuenta { char numero[20]; char titular[80]; long int saldo; float interes;
public:
void ingreso (long int cantidad); void reintegro (long int cantidad); void ingreso_interes (void); int esta_en_rojos (void); long int ver_saldo (void);
};
void ingreso (long int cantidad); void reintegro (long int cantidad); void ingreso_interes (void); int esta_en_rojos (void); };
Es mucho ms seguro el uso de funciones miembro de acceso a las variables miembro, aunque puede suponer una pequea penalizacin en el tiempo de acceso necesario.
Clases amigas
A veces dos clases estn tan ntimamente relacionadas que una requiere acceso total a varios atributos y operaciones privados de la otra. Este acceso puede realizarse proporcionando las operaciones pblicas necesarias, pero esto puede resultar engorroso e innecesario, pues slo esta clase requiere estas operaciones. La clase InformeCuenta realiza un informe impreso del estado de una Cuenta con todos sus datos. Para ello necesita acceder a todos los atributos de la clase, pero stos son privados y no existe en todos los casos operaciones para obtener su valor. Solucin: hacer la clase InformeCuenta amiga de Cuenta para que tenga acceso completo a sus atributos. Hacer una clase amiga de otra es permitir selectivamente la violacin de las reglas de ocultacin de informacin.
class Cuenta { char numero[20]; char titular[80]; long int saldo; float interes; public: friend class InformeCuenta;
A partir de ahora la clase InformeCuenta es amiga de Cuenta y tiene acceso completo a sus atributos privados
void ingreso (long int cantidad); void reintegro (long int cantidad); void ingreso_interes (void); int esta_en_rojos (void); long int ver_saldo (void);
};
La declaracin de clases amigas debe realizarse con moderacin y slo en casos en que est plenamente justificado.
Fichero cuenta.cpp
void Cuenta::ingreso (long int cantidad) { saldo += cantidad; }
void Cuenta::reintegro (long int cantidad) { saldo -= cantidad; Calificador de clase. Indica la } clase que contiene la funcin miembro
void Cuenta::ingreso_interes (void) { saldo += saldo * interes / 100; El acceso a una variable de clase } es directo, como si se tratara de una variable local definida en int Cuenta::esta_en_rojos (void) cada una de las funciones { miembro. Es equivalente a: return saldo < 0; ::saldo -> Indicacin explcita de } que se trata de una variable de clase. long int Cuenta::ver_saldo (void) { Cuenta::saldo -> Igual que la return saldo; forma anterior pero indicando } cual es la clase a la que pertenece.
Generacin de cdigo para una llamada a una funcin miembro ordinaria ...
Generacin de cdigo para una llamada a una funcin miembro ordinaria ...
...
Una llamada a una funcin miembro inline es mucho ms rpida ya que se elimina el salto, el retorno y introduccin en la pila del resultado
class Cuenta { char numero[20]; char titular[80]; long int saldo; float interes; public: void ingreso (long int cantidad); void reintegro (long int cantidad); void ingreso_interes (void); int esta_en_rojos (void) { return saldo < 0; } long int ver_saldo (void) { return saldo; } };
// ----------------------------------------------------------// Cuenta.cpp - Implementacin de la clase Cuenta // ----------------------------------------------------------#include "cuenta.hpp" void Cuenta::ingreso (long int cantidad) { saldo += cantidad; }
Es necesario poder inicializar un objeto antes de comenzar a trabajar con l. Las tareas tpicas en la inicializacin son:
Inicializacin de atributos Asignacin de memoria dinmica Apertura de archivos
void Cuenta::inicializar (char *anumero, char *atitular, long int asaldo, float ainteres) { strcpy (numero, anumero); strcpy (titular, atitular); saldo = asaldo; interes = ainteres; }
Esta no es una solucin limpia: - Obliga al usuario a conocer cual es la funcin miembro de inicializacin del objeto, que puede tener muchos nombres: inicializa, inicializar, nueva, crear. - Si el usuario olvida llamar a la funcin miembro de inicializacin tras crear el objeto surgirn muchos problemas...
La programacin orientada a objetos proporciona un medio de inicializacin ms normalizado y mucho ms seguro: los constructores. Un constructor es una operacin especial de la clase que se llama automticamente durante la creacin del objeto. En C++ es fcilmente reconocible porque tiene el mismo nombre que la clase. class Cuenta { char numero[20]; char titular[80]; long int saldo; float interes; public: Cuenta (char *anumero, char *atitular, long int asaldo, float ainteres); // Constructor void ingreso (long int cantidad); void reintegro (long int cantidad); void ingreso_interes (void); int esta_en_rojos (void) { return saldo < 0; } long int ver_saldo (void) { return saldo; } };
La implementacin del constructor de nuestra clase sera la siguiente: Cuenta::Cuenta (char *anumero, char *atitular, long int asaldo, float ainteres) { strcpy (numero, anumero); strcpy (titular, atitular); saldo = asaldo; interes = ainteres; } Mltiples constructores Muchas veces resulta muy interesante definir varios mtodos de inicializacin para una clase. Por ejemplo, una clase Imagen puede ser inicializada de varias formas: - Crear una imagen vaca, indicando el tamao en pixels y el nmero de colores. - Crear una imagen recuperandola desde un fichero en disco, dado el nombre del mismo. - Crear una imagen mediante una operacin pegndola desde el portapapeles del sistema.
class Cuenta { char numero[20]; char titular[80]; long int saldo; float interes;
La sobrecarga de funciones de C++ permite que puedan definirse varios constructores para una clase, siempre y cuando se diferencien al menos en el tipo o nmero de argumentos.
public: // Constructor para cualquier tipo de cuentas Cuenta (char *anumero, char *atitular, long int asaldo, float ainteres); // Constructor para cuentas corrientes (interes = 1) Cuenta (char *anumero, char *atitular, long int asaldo); void ingreso (long int cantidad); void reintegro (long int cantidad); void ingreso_interes (void); int esta_en_rojos (void) { return saldo < 0; } long int ver_saldo (void) { return saldo; } };
La implementacin del segundo constructor sera: Cuenta::Cuenta (char *anumero, char *atitular, long int asaldo) { strcpy (numero, anumero); strcpy (titular, atitular); saldo = asaldo; interes = 1; } Vamos a aadir un tercer constructor para leer los datos de la cuenta desde un fichero: class Cuenta { char numero[20]; char titular[80]; long int saldo; float interes; public: // Constructor para nuevas cuentas de cualquier tipo Cuenta (char *anumero, char *atitular, long int asaldo, float ainteres);
// Constructor para nuevas cuentas corrientes (interes = 1) Cuenta (char *anumero, char *atitular, long int asaldo);
Una limitacin especialmente grave de C++ y Java es que no pueden definirse dos constructores que reciban los mismos parmetros aunque signifiquen cosas distintas. Por ejemplo, no podramos aadir un constructor para cuentas de ahorro (con un inters fijo al 8%): Cuenta::Cuenta (char *anumero, char *atitular, long int asaldo) { strcpy (numero, anumero); strcpy (titular, atitular); saldo = asaldo; interes = 8; } Los parmetros coinciden con los del constructor para cuentas corrientes, por tanto el compilador no puede distinguir entre ambas funciones y producir un error de compilacin.
En el lenguaje Eiffel esto es posible, ya que cada constructor puede tener un nombre distinto: -- Constructor general de la clase Cuenta crear (anumero, atitular: STRING; asaldo: INTEGER; ainteres: REAL) is do ... End -- Constructor para cuentas corrientes (interes = 1) crear_corriente (anumero, atitular: STRING; asaldo: INTEGER) is do ... end -- Constructor para cuentas de ahorro (interes = 8) crear_ahorro (anumero, atitular: STRING; asaldo: INTEGER) is do ... end
Cuando un argumento puede tomar o no un valor por defecto pueden utilizarse argumento por omisin. El nmero de argumentos por defecto puede variar, pero siempre quedar agrupados al final de la lista de parmetros. En el siguiente constructor de la clase cuenta, el ltimo argumento posee un valor por omisin. A una cuenta que no se le proporcione un inters de forma explcita, quedar con el valor del argumento por omisin. Cuenta::Cuenta (char *anumero, char *atitular, long int asaldo, ainteres = 1) { strcpy (numero, anumero); strcpy (titular, atitular); saldo = asaldo; interes = ainteres; }
Constructores por defecto Un constructor por defecto u omisin es aquel que no requiere argumentos. La lista de argumentos es o bien vaca, o todos los argumentos tienen valores por omisin asignados a ellos. Se utilizan para poder inicializar objetos de alguna manera, aunque sus atributos no posean los valores definitivos. Argumentos con valores por omisin: class fecha{ int dd, mm, aa; public: fecha(int d=0; int m=0; int a=0):dd(d),mm(m),aa(a){} void escribir(){cout<<dd<<"/"<<mm<<"/"<<aa<<endl;} }
Constructor copia Un constructor copia es aquel que sirve para crear un objeto exactamente igual que otro. Cuenta::Cuenta (Cuenta& c) { strcpy (numero, c.numero); strcpy (titular, c.titular); saldo = c.saldo; interes = c.interes; }
El compilador proporciona siempre un constructor copia por defecto. Este constructor simplemente copia los valores de los atributos del objeto que se pasa como argumento. Cuando esto no sea suficiente, proporcionaremos un constructor copia con asignaciones vlidas
Podra usarse nuevamente una operacin como destruye, finaliza o libera para este fn, pero los inconvenientes seran los mismos que los existentes en el caso de la inicializacin. Los lenguajes orientados a objetos proporcionan un tipo de operaciones especiales denominados destructores de la clase, que se llaman automticamente cuando un objeto es destruido.
Al contrario que en el caso de los constructores, normalmente solo existe un nico destructor. En C++ el destructor tiene el mismo nombre que la clase, precedido por el carcter ~ (ASCII 126) y nunca tiene argumentos en la llamada. // Un Array dinmico para enteros muy simple. class ArrayIntDin { int *mem; long int tam; public: // Constructor, asignacin de memoria dinmica // para el buffer del array ArrayIntDin (long int atam) { mem = new int[tam = atam]; } // Destructor del buffer ~ArrayIntDin () { delete mem; } // Lectura y escritura en el buffer int leer (long int pos) { return mem[pos]; } void escribir (long int pos, int valor) { mem[pos] = valor; }
};
Tras operar con una cuenta, sera necesario que se salvara de manera permanente en disco. As cuando se vuelva a recuperar, su saldo y dems caractersticas se mantendrn justo como estaban tras la ltima actualizacin: class Cuenta { char numero[20]; char titular[80]; long int saldo; float interes; public: // Constructores Cuenta (char *anumero, char *atitular, long int asaldo, float ainteres); Cuenta (char *anumero, char *atitular, long int asaldo); Cuenta (char *anumero); // Destructor de la cuenta. Salva el estado de la cuenta en disco ~Cuenta (); void ingreso (long int cantidad); // Operaciones de la clase void reintegro (long int cantidad); void ingreso_interes (void); int esta_en_rojos (void) { return saldo < 0; } long int ver_saldo (void) { return saldo; } };
Cuenta::~Cuenta () { char nombref[80]; strcpy (nombref, numero); strcat (nombref, ".cnt"); // Abrir el fichero de la cuenta para escritura // (Obviamos la comprobacin de errores) ofstream out (nombref); // Escribir los valores de las variables miembro out << titular <<endl<< saldo <<endl<< interes <<endl; }
Si una clase no requiere ninguna finalizacin en especial, no es necesario incluir un destructor sin cdigo en la interfaz de la clase:
~Cuenta() {}