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. 

No hay comentarios:

Publicar un comentario