APUNTES PROII 2º PARCIAL. MEMORIA DINÁMICA. Hay que distinguir entre: Estática: memoria que se reserva en tiempo de compilación. Dinámica: memoria que se reserva en tiempo de ejecución. 5 pasos: 1. 2. 3. 4. 5. Declaración de un puntero en la zona de atributos. Reservar con el operador new la memoria en el constructor. Validar que tenemos espacio en la memoria en el constructor. Utilizar esa zona de memoria en donde queramos. Eliminar la reserva con el operador delete en el destructor. Ejemplo: int main() { int num; int * puntero; //Paso 1. cout << "Introduzca el num de elementos:" cin num; puntero = new (nothrow) int [num]; //nothrow devuelve un null si no se ha podido reservar esa memoria //Paso 2. if(puntero == null){ cerr << "Error de memoria dinámica"; exit (-1); } //Paso 3. for ( int i = 0; i < num; i++ ) { cout << "Introduzca el valor" << i << endl; cin puntero[i]; // equivalente a *(puntero+i) } //Paso 4 delete [] puntero; //Paso 5. return 1; } SOBRECARGA DE OPERADORES. 1. FORMAS DE SOBRECARGAR. - Usando funciones amigas. - Usando funciones miembro. Funciones friend: es la función externa a la clase que tiene acceso a la parte privada de esta. 2. TIPOS DE OPERADORES. Operadores unarios. Ej.: a++; Sobrecargar como función friend: friend valor_retorno operator++(tipo_operando a); //prototipo de la función Sobrecargar como función miembro: valor_retorno operator++(); Operadores binarios. Ej.: a + b; Sobrecargar como función friend: friend valor_retorno+(tipo1_operador a, tipo2_operador b); //prototipo de la función Sobrecargar como función miembro: valor_retorno operator+(tipo_operator b); Ejemplo: Como función miembro: Complejo a(5.3, 2); Complejo b; a = a + b; Dentro de la clase Complejo: Prototipo: Complejo operator+(Complejo b); Su implementación: Complejo Complejo::operator+(Complejo b){ Complejo toret; toret.real = real + b.real; toret.imaginaria = imaginaria + b.imaginaria; return toret; } Como función amiga: friend Complejo operator+(Complejo a, Complejo b); //Prototipo dentro de la clase Complejo operator+(Complejo a, Complejo b) { //Implementación de la función friend Complejo toret; toret.real = a.real + b.real; toret.imaginaria = a.imaginaria + b.imaginaria; return toret; } Sobrecarga de operadores <<, >>. Siempre se implementan como función friend: Sobrecarga de << (operador de inserción): Como valor de retorno siempre ostream por referencia. El primer argumento es un flujo de salida y el segundo lo que queremos sacar por pantalla. Prototipo para la clase catalogo: friend ostream & operator<<(ostream & o, Catalogo c); Sobrecarga de >>(operadir de extracción): Como valor de retorno istream por referencia Prototipo para la clase catálogo: friend istream & operator>>(istream & i, Catalogo & c); En el código de la sobrecarga, primero se implementa el destructor y luego el código del constructor de copia. GESTIÓN DE EXCEPCIONES. Hay dos tipos de excepciones: las que genera el sistema y las que generamos nosotros. Sintaxis: try { //aquí irán todas las sentencias en las que se puede generar una excepción+ if(.....) { throw nombre excepción //lanza una excepción }catch(tipo 1 arg 1){ //puede o no tener argumentos. Es el manejador de excepciones. }catch(tipo 2 arg 2){ // un try puede tener asociados varios catch }catch(...){ //catch universal, maneja cualquier tipo de excepción } Ejemplo: try { if ( temperatura < 0 ) throw "Mucho frío"; else if ( temperatura > 0 && temperatura < 30 ) throw temperatura; else throw "Mucho calor"; }catch ( string c ) { cout << c; }catch ( int c ) { cout << c; }catch ( ... ) { ... } Se suele poner un único manejador en el main que controle todas las excepciones del programa. #include <exception> //La clase exception tiene una funcion definida what(); que visualiza la pila, hace un volcado de pila. bad_alloc int * puntero; try{ puntero = new int [10]; }catch(const bad_alloc & e){ cerr << e.what() << endl; } Clase excepción: class Excepcion1{ public: Excepcion1(const string &d){ descripcion = d; } //suele tener 2 constructores //aquí se le pasaría const char * descripcion void visualizar(){ cerr << descripcion; } private: string descripcion; o char * descripcion }; HERENCIA Y COMPOSICIÓN. Tipos de relaciones existentes entre clases. Herencia = es_un Composición = contiene_un Se permiten 2 tipos de herencia. 1. Herencia simple. Clase base o clase padre + una o mós clases derivadas o hijo. Una clase únicamente puede heredar de una clase. Ejemplo: class Persona { private: int edad; }; class Becario { private: float beca; }; Becario heredará de persona. SINTAXIS: class Derivada: modificador clase_Base { public: Derivada(arg Base + arg derivada): Base(nombre arg Base) { ..... } //Lo que haga falta private: //Lo que haga falta. } Ejemplo: class Persona { public: Persona(){edad=0} Persona(int e){edad=e} private: int edad; }; class Becario:public Persona { public: Becario(){beca = 0.0} Becario(int e, float b):Persona(e) { beca = b; } private: float beca; } MODIFICADOR public protected private CLASE BASE public protected private public protected private public protected private CLASE DERIVADA public protected INACCESIBLE protected protected INACCESIBLE private private INACCESIBLE 2. Herencia múltiple. Varias clases padre. Sintaxis: class Derivada:public Base1, public Base2 { public: Derivada(tipo nombre):Base1(nombre_atributo),Base2(nombre_atributo) { .... } }; En el archivo .h pondríamos: class Derivada:public Base1, public Base2{ public: Derivada(tipo nombre); }; En herencia múltiple no podemos separar en .h y .ccp por que se producen errores. LIGADURA DINAMICA. Hay 2 tipos de funciones virtuales: puras o normales. A las puras le asignamos la dirección 0 o null, sin código. En las normales se da la opción en la clase derivada de implementar o no. Si en la clase derivada se implementa se llama a esa, si no se llamará a la de la clase base. La clase en la que haya una función virtual se convierte en abstracta y su constructor se convierte en virtual. No podemos crear objetos de la clase base. La ligadura dinámica retrasa hasta tiempo de ejecución el linkado de una función con su código. Pasos: 1. Crear un puntero que apunte a una clase base que contenga alguna función virtual. 2. Darle la dirección de un objeto de una clase derivada al puntero. 3. Acceder mediante -> a la función virtual.