martes, 10 de enero de 2017
Capítulo 18. Preprocesador
El preprocesador Cuando se compila un programa de c++ previamente se hace un
preprocesamiento en el que se revisan determinadas variables de preprocesador. Con
ellas lo que se consigue es que el compilador modifique el codigo fuente del programa
antes de crear el ejecutable. Vamos varios usos utiles.
/**
* Preprocesador.cpp
* Programa c++ que muestra el uso del preprocesador.
*
* Pello Xabier Altadill Izura
* Compilacion: g++ -o Preprocesador Preprocesador.cpp
*
*/
// include se utiliza para poder utilizar codigo externo,
// generalmente las librerias standar o nuestras propias librerias
using namespace std;
#include
// Las variables de preprocesador sirven para que el compilador haga ciertas
// modificaciones en el codigo fuente
#define PI 3.1415
#define BATMAN "Bruce Wayne"
#define MISTERX "Felipe Gonzalez"
#define REVELAR
#define BUFFER 255
// podemos definir FUNCIONES, aunque sin complicarlas ya que dificulta
// la depuracion y se pasa el tipado de variables por el arcoltriunfo
#define PORCENTAJE(a,b) (a*b)/100
// Guardias de inclusion
// Estructura condicional para evitar multiples inclusiones
// La siguiente structura comprueba si NO se ha definido la variable FSTREAM
#ifndef FSTREAM
// si no se ha definido, la definimos
#define FSTREAM
#include
#endif // fin de condicion
// macro de comillas:
#define write(x) cout << #x << endl;
int main () {
int i = 345;
float var = 4.67;
char buffer[BUFFER]; // automaticamente el compilador traduce: buffer[255]
#ifdef PI
cout << "El valor PI es: " << PI << ": ten fe en el caos" << endl;
#else
95
Capítulo 18. Preprocesador
cout << "PI no esta definido..." << endl;
#endif
// ahora miramos una variable de preprocesador que no esta:
// y asi en este caso no se revelamos quien es BATMAN...
#ifdef REVELAR
cout << "Batman realmente se trata de: " << BATMAN << endl;
#endif
// con esta orden eliminamos la variable:
#undef REVELAR
// y este es el efecto:
#ifdef REVELAR
cout << "MisterX realmente es: " << MISTERX << endl;
#endif
cout << "var * PI = " << (var * PI) << endl;
// mostramos la llamada a la funcion
cout << "Porcentaje 15% de "<< i << " es: " << PORCENTAJE(i,15) << endl;
// llamada a la macro. Atencion, convertira MISTERX?
write(Hay que ver que lujo verdad MISTERX);
return 0;
}
Macros para depuracion Disponemos de algunas variables de macro que facilitan la
depuracion asi como de la funcion assert. Veamos el uso
/**
* Depurador.cpp
* Programa c++ que muestra el uso del preprocesador para depurar
*
* Pello Xabier Altadill Izura
* Compilacion: g++ -o Depurador Depurador.cpp
*
*/
// include se utiliza para poder utilizar codigo externo,
// generalmente las librerias standar o nuestras propias librerias
using namespace std;
#include
// Disponemos de estas variables de macro predefinidas, muy utiles para depurar.
// __DATE__ : sustituye esa variable por la fecha
// __TIME__ : sustituye esa variable por la hora
// __LINE__ : sustituye esa variable por la linea de programa
// __FILE__ : sustituye esa variable por el nombre del fichero del programa
// definimos la variable DEBUG para activar la depuracion
#define DEBUG
// y mostramos el uso de assert(), su disponibilidad dependera del compilador.
// cuando invocamos la funcion assert, si lo que tiene como parametro es TRUE
96
Capítulo 18. Preprocesador
// no habra problema pero si es false saltara un codigo de depuracion que le digamos
#ifndef DEBUG
#define ASSERT(x)
#else
#define ASSERT(x) \
if (! (x)) { \
cout << "error detectado, fallo: " << #x << "\n"; \
cout << " linea" << __LINE__ << " del fichero " <<
__FILE__ << "\n"; \
}
#endif
// funcion principal para las pruebas:
int main () {
int i = 345;
float var = 4.67;
cout << "hola hoy es: " << __DATE__ << endl;
ASSERT(i>5);
cout << "Este es el fichero: " << __FILE__ << endl;
cout << "Estamos en la linea: " << __LINE__ << endl;
ASSERT(i==0);
return 0;
}
97
Capítulo 18. Preprocesador
98
Capítulo 19. Principios de POO
Programacion orientada a objetos Es probable que te toque hablar con amiguetes que
programan en la lengua de Mordor (visualbasic) o gente que programa en c ofuscado,
o lo que es peor, desconocidos que te dicen que "programan" en HTML; estos
intercambios de experiencias, esas afirmaciones sobre rendimientos de ejecucion
pueden hacer tambalearse los cimientos de tu fe en la POO. Gracias a estas anotaciones
rescatamos del olvido las excelencias de la POO y nos armamos de argumentos
ante los herejes que nos salgan al paso con listados de codigo en ristre.
• Encapsulacion: los detalles de implementacion estan ocultos. Esto reduce que se
reproduzcan errores cuando se hacen cambios. Se facilita enormementa la interaccion
entre otros objetos encapsulados ya que no tienen que conocer los detalles de
uso.
• Herencia: este mecanismo es una de las claves de la OOP. Todos los atributos,
metodos, programaciones contenidas en una clase pueden heredarse y extenderse
a otras clases facilitando la reutilizacion de codigo. Cualquier cambio se propaga
en toda la jerarquia de clases y nos vuelve a ahorrar trabajo haciendo un sistema
mas facil de mantener.
• Polimorfismo:gracias a el facilitamos la ampliacion del sistema ya que en lugar de
crear codigo por cada tipo de dato lo podemos agrupar todo en uno utilizando la
sobrecarga de funciones. El copy-paste puede parecer lo mismo, pero a la hora de
cambiar/mantener un sistema se acaban metiendo muchas horas, cosa que con la
POO evitas.
Bueno, imaginemos que queremos desarrollar un sistema utilizando la orientacion
a objetos. ¿Por donde se empieza? Esta sería una aproximación: Todo proyecto
comienza con una descripcion que encierra entre sus lineas los requerimientos del
sistema. Es el momento de tomar un subrayador y abrir el tercer ojo; lo primero que
debemos hacer es identificar objetos potenciales que formaran el diseño y es tan facil
como buscar sustantivos (nombres, cosas). A veces no resulta tan obvio ya que los
objetos pueden manifestarse de diversas maneras:
• Cosas
• Entidades externas (personas, maquinas o incluso otros desarrollos)
• Eventos (Cazo o zuzezo :>)
• Roles
• Lugares
• Organizaciones (dpto, division)
Debemos acotar los objetos en un dominio cerrado y ser capaces de identificar lo que
esos objetos son, saben y hacen. Partiendo de la identificacion de objetos se puede ir
desarrollando un diseño de clases usando simbolos o lenguajes unificados tales como
UML. Aunque realmente no hay que forzarse, la POO no es mas que otra forma mas
de abordar un problema; puede que el diseño OO te salga por instinto. No es de extrañar
que un programador con años de experiencia acaba recurriendo a desacoplar
cada vez mas sus modulos y a recurrir a patrones de software sin darse cuenta. Lo
que no se puede negar es el auge de la POO viendo la proliferacion de lenguajes
OO, o su adaptacion para tener las ventajas de su punto de vista (Java, php, python,
perl,... son lenguajes "recientes")
A veces puedes liarte al tratar de distinguir clase y objeto; esta cita del profeta resuelve
las dudas:
Mientras que un objeto es una entidad que existe en el tiempo y en el espacio, una
clase representa solo una abstraccion,"la esencia" del objeto si se puede decir asi.
Grady Booch
99
Capítulo 19. Principios de POO
Figura: la incesante busqueda del santo grial...
En fin, estas anotaciones (eufemismo de TXAPA) sirven para acordarse de porque
c++ puede ser una herramienta util.
100
Capítulo 20. Templates
Gracias a c++ podemos definir clases-plantilla: son clases PARAMETRIZABLES por
lo general entidades abstractas que se pueden concretar en algo mas concreto. El
ejemplo mas claro es de las estructuras de datos tradicionales: Pilas, Listas, Colas,
etc.. Esas estructuras pueden contener distintos tipos de datos: enteros, strings, objetos,...
Debemos reescribir la logica de cada estructura para cada tio de dato? NO!
Podemos definir una clase plantilla para la Lista, la cola, la pila etc, y luego simplemente
invocarlas especificando el tipo de dato. Asi de facil.
Figura: un caballero de la orden de los Templates
Veamos este horrible ejemplo de lista (atencion a la complicadilla sintaxis)
/**
* Lista.hpp
* Clase que define una estructura de datos lista Generica
*
* Pello Xabier Altadill Izura
*/
using namespace std;
#include
// Asi es como declaramos una clase plantilla
// template class NombreClase
template class Lista {
public:
101
Capítulo 20. Templates
// Constructor
Lista();
// Constructor
Lista(GENERICO elemento);
// Constructor copia
Lista(Lista const &);
// Destructor
~Lista();
// agregar elemento
void agregar(Lista *nodo);
// se mueve hasta el siguiente dato
Lista* siguiente();
// comprueba si existe un elemento
bool existe(GENERICO dato);
// comprueba si existe un elemento
GENERICO getDato() { return this->dato;}
private:
// un elemento que apunta a otra lista, asi sucesivamente
Lista *ladealao;
// el dato es del tipo GENERICO
GENERICO dato;
};
Y su implementacion
/**
* Lista.cpp
* Programa que implementa la clase de Lista generica
*
* Pello Xabier Altadill Izura
* Compilacion: g++ -c Lista.cpp
*
*/
#include "Lista.hpp"
// En la implementacion debemos detallar el tipo de dato,
// especificando todo el tema de plantilla, o sea que en lugar
// de poner Lista:: delante de cada funcion debemos poner TODO
// el churro siguiente
// template Lista::nombreFuncion
// Constructor
template Lista::Lista() {
ladealao = 0;
//dato = 0;
cout << "Nueva lista creada." << endl;
}
102
Capítulo 20. Templates
// Constructor
template
Lista::Lista(GENERICO elemento) {
ladealao = 0;
dato = elemento;
cout << "Nueva lista creada. Dato inicial: " << dato << endl;
}
// Constructor copia
template Lista::Lista(Lista
const & original) {
ladealao = new Lista;
ladealao = original.ladealao;
dato = original.dato;
}
// Destructor
template Lista::~Lista() {
}
// agregar elemento: AL LORO con donde se pone el retonno
template void
Lista::agregar(Lista *nodo) {
nodo->ladealao = this;
ladealao = 0;
}
// se mueve hasta el siguiente dato
template Lista*
Lista::siguiente() {
return ladealao;
}
//Lista template Lista::siguiente();
// comprueba si existe un elemento
template bool
Lista::existe(GENERICO dato) {
return false;
}
Usando la lista Y ahora definimos una clase llamada Nombre. Crearemos una lista
de nombres. Este es la definicion
/**
* Nombres.hpp
* Clase que define los nombres. No es mas que una
cobaya para probar el template
*
* Pello Xabier Altadill Izura
103
Capítulo 20. Templates
*/
// Esta clase la usaremos en el template, no hay que definir nada en especial
class Nombre {
public:
// Constructor
Nombre():nombre("Jezabel") {}
// Constructor
Nombre(char *nombre) {
this->nombre = nombre;
}
// Constructor copia
Nombre(Nombre const &);
// Destructor
~Nombre(){}
// agregar elemento
char* getNombre() const { return this->nombre;}
private:
// el dato
char *nombre;
};
Y su implementacion y los ejemplos de uso de plantillas
/**
* Nombres.cpp
* Programa que implementa la clase nombres y utilza los templates
* para crear una lista de nombres.
*
* Pello Xabier Altadill Izura
* Compilando: g++ -o Nombre Lista.o Nombre.cpp
*/
#include "Nombre.hpp"
#include "Lista.hpp"
// Constructor copia
Nombre::Nombre(Nombre const & original) {
nombre = new char;
nombre = original.getNombre();
}
// Funcion principal para las pruebas
int main () {
// Asi es como se implementan objetos con clases plantilla
Lista listanombres;
104
Capítulo 20. Templates
Lista *tmp, *final;
Nombre test = Nombre("Prince");
// podemos definir Listas de cualquier tipo basico
Lista listaenteros;
// guardamos la posicion inicial; final es un puntero, le pasamos la direccion
final = &listanombres;
// vamos a crear unos cuantos NODOS y los añadimos
tmp = new Lista;
tmp->agregar(final);
final = tmp;
// otra mas...
tmp = new Lista;
tmp->agregar(final);
final = tmp;
// otra mas...
tmp = new Lista;
tmp->agregar(final);
final = tmp;
// y ahora recorremos la lista:
tmp = &listanombres;
while (tmp) {
cout << tmp->getDato().getNombre() << endl;
tmp = tmp->siguiente();
}
return 0;
}
Es un tema complejo pero util.
105
Capítulo 20. Templates
106
Capítulo 21. Excepciones
Capturando excepciones Las excepciones son un mecanismo de c++ para capturar
errores que se producen en tiempo de ejecucion. Un programa puede estar bien hecho
pero por causas exogenas pueden producirse errores. Mediante este sistema hacemos
que el codigo sea mucho mas ROBUSTO.
/**
* Excepciones.cpp
* codigo que muestra como capturar excepciones y evitar
que el programa
* finalice inesperadamente.
*
* Pello Xabier Altadill Izura
*
*/
using namespace std;
#include
#include
#include
// programa principal, para las pruebas
int main () {
int i;
float flotante;
char *palabra;
char buffer[5];
ifstream ficheroInexistente;
// para capturar excepciones debemos crear un bloque try-catch
// que englobe algun momento problematico o critico del programa:
// try { codigo; } catch(TipoDeError) { codigo_corrector; }
// lo habitual suele ser alguna situacion que dependa de la existencia
// o no de un fichero, la entrada de datos de un usuario, etc..
// El programa no puede controlar lo que le meten, pero puede estar
// preparado para el error, reconducir la ejecucion y corregir la situacion
try
{ // inicio del bloque. Preparamos una serie de putadas...
cout << "Mete lo primero que se te ocurra, distinto de float: " << endl;
cin >> flotante;
char * buff = new char[100000000];
ficheroInexistente.open("MotorDeAgua.txt");
ficheroInexistente.getline(buffer,255);
ficheroInexistente.close();
}
catch(std::bad_alloc& error_memoria)
{
cout << "Error de asignacion" << error_memoria.what() << endl;
} // podemos seguir capturando
catch (std::exception& stdexc)
{ // este es el tipo de error que se espera
// y entre llaves metemos el codigo que se ejecuta en caso de error.
cout << "Error general, mensaje: " << stdexc.what() << endl;
}
107
Capítulo 21. Excepciones
return 1;
}
Excepciones personalizadas Una clase puede definir sus propias excepciones. Un
mecanismo muy util para malos usos de los objetos. Definimos la clase coche y
preparamos el codigo para capturar posibles fallos debidos a la falta de combustible.
/**
* Coche.hpp
* Definicion de la clase coche, en la que se muestra el
uso de excepciones
*
* Pello Xabier Altadill Izura
*
*/
#include
class Coche {
public:
Coche();
Coche(char *m,int cil,int cab, int litros);
~Coche();
void arranca();
void echaCombustible();
void detiene();
void acelera();
private:
char *marca;
int cilindrada;
int caballos;
int litrosCombustible;
};
// clase exclusiva para excepciones.
// Nota: la podemos definir DENTRO de la Clase coche, como un atributo MAS
class Averia {
public:
// constructor
Averia():mensaje("Error") {}
// constructor con mensaje
Averia(char *mensaje) {
108
Capítulo 21. Excepciones
this->mensaje = mensaje;
}
char* dimeQuePasa() { return this->mensaje; };
private:
char *mensaje;
};
Y la implementacion
/**
* Coche.cpp
* Implementacion de la clase coche, en la que se
muestra el uso de excepciones
*
* Pello Xabier Altadill Izura
* Compilacion: g++ -o Coche Coche.cpp
*/
#include "Coche.hpp"
Coche::Coche() {
cout << "Coche creado." << endl;
}
Coche::Coche (char *m,int cil,int cab, int litros) {
marca = m;
cilindrada = cil;
caballos = cab;
litrosCombustible = litros;
cout << "Coche creado." << endl;
}
Coche::~Coche() {
cout << "Coche destruido." << endl;
}
// el coche arranca
void Coche::arranca() {
// si no hay combustible: EXCEPCION!
if (litrosCombustible == 0) {
throw Averia();
}
litrosCombustible--;
cout << "Arrancando: brummm! " << endl;
109
Capítulo 21. Excepciones
}
// el coche se detien
void Coche::detiene() {
cout << "Deteniendo coche " << endl;
}
// el coche acelera
void Coche::acelera() {
if (litrosCombustible == 0) {
throw Averia("No puedo acelerar sin combustible");
}
cout << "Acelerando: BRRRRRUMMMMmmmmmmmmh!! " << endl;
}
// funcion principal para pruebas
int main () {
int i;
Coche buga("Seat",250,1300,0);
Coche tequi("Audi",260,1500,1);
// vamos a arrancar el coche pero si algo falla
// capturamos la excepcion
try {
buga.arranca();
} catch (Averia excepcion) {
cout << "Excepcion. Jar que no puedo. " << endl;
}
// arracamos el tequi
tequi.arranca();
// provocamos la excepcion y la capturamos mostrando la explicacion.
try {
buga.acelera();
} catch (Averia excepcion) {
cout << "Jar que no puedo. " << excepcion.dimeQuePasa() << endl;
}
return 0;
}
110
Capítulo 21. Excepciones
Figura: el control de excepciones nos proporciona mas robustez.
Suscribirse a:
Enviar comentarios (Atom)
No hay comentarios:
Publicar un comentario