Introducción a la Programación Orientada a Objetos

Anuncio
Francisco Javier Peña Escobar
Universidad del Valle
[email protected]
Universidad del Valle - 2009
Relaciones entre clases







Relaciones entre clases. Herencia.
Constructores y destructores. Lista de
inicialización en constructor.
Funciones protected.
Funciones virtuales y virtuales puras (clases
abstractas).
Destructores virtuales.
Polimorfismo.
Herencia múltiple (no usarla).

Son siempre permanentes en C++, nunca
temporales. Son:
◦ “Es un”.
◦ “Es parecido a un”


Ambas se implementan con el mecanismo de
“herencia pública” (con el operador dos puntos :)
Ejemplo: La clase derivada hereda de la clase base
class Base
{
private:
// etc...
public:
// etc...
};
class Derivada : public
Base
{
private:
// etc...
public:
// etc...
};




Lo que hace el mecanismo de herencia es
incorporar la clase Base dentro de la clase
Derivada
La clase Derivada entonces tiene todo lo que
tenía la clase Base, y además puede añadir sus
propios métodos en la parte pública y sus
propios atributos en la parte privada.
Si la clase Derivada añade nuevas funciones o
atributos, además de las recibidas en la clase
base, entonces es una relación “es parecido a”.
Si la clase Derivada no añade nuevas funciones
no atributos entonces es una relación “es un”.
InstrumentoMusical
PÚBLICO
es un
PIANO
Persona
es un
es un
es parecido
a un
Músico
DIRECTOR DE
ORQUESTA
es un
GUITARRISTA
GUITARRA
es un
PIANISTA
PARTITURA

El constructor de la clase derivada debe
llamar al constructor de la clase base,
pasándole los argumentos adecuados.
◦ El constructor de la clase derivada llama
implícitamente al de la clase base si el constructor
de la clase base no requiere argumentos (no hay
que escribir ningún código especial).
◦ El destructor de la clase derivada llama
implícitamente al destructor de la clase base (no
hay que escribir ningún código especial).

El orden de llamada a constructores y
destructores es el lógico:
◦
◦
◦
◦
◦
Constructor de la clase Base
Constructor de la clase Derivada
....
Destructor de la clase Base
Destructor de la clase Derivada

La parte privada de la clase Base sigue siendo
inaccesible incluso para la parte Derivada. Si se desea
que haya en la clase Base una parte inaccesible para
el resto del mundo, pero accesible para la parte
Derivada, debe declararse protected.
protected
public
private
private

Hay dos estilos de trabajo con la herencia que
los vamos a nombrar usando una analogía
con el mundo biológico de las células:
◦ Estilo “procariota”: Pasar todos los atributos private
a protected: Con ello la clase Derivada es una clase
“mas gorda”, con mas atributos en su interior ya
que ha roto la cápsula de la clase Base:
atributo1_Base
atributo1_Derivada
atributo2_Base
atributo2_Derivada
◦ Estilo “eucariota”: No usar protected nunca: Ello
significa que la clase Base permanece encapsulada
e independiente de la clase Derivada:
atributo1_Derivada
atributo2_Derivada
atributo1_Base
atributo2_Base
◦ Estilo mixto: Con parte public, private y protected



Nota: Procariotas son las células sin núcleo
(usualmente bacterias). Eucariotas son las células
con núcleo y otros órganos internos (usualmente
las células de los animales y plantas superiores).
Parece ser que algunas células primitivas
eucariotas trataron de devorar otras células
semejantes, pero no consiguieron digerirlas del
todo, quedando como “órganos internos” que
trabajan colaborativamente (simbiosis). Este fue
el primer acto sexual que dio origen a las células
eucariotas.
Obvia decir que las células eucariotas son mucho
mas evolucionadas, y esperemos que el software
OO que ustedes hagan también lo sea.
Núcleo (con ADN)
Mitocondrias (respiran oxígeno)
CÉLULA EUCARIOTA
CÉLULA PROCARIOTA
En las células de plantas: cloroplasto
(antiguas cianobacterias)

Si una función de la clase Base no hace lo que
deseamos para la clase Derivada, se puede
reescribir (overwrite) en la clase Derivada.
class Base
{
public:
double funcion(int x);
};
class Derivada : public Base
{
public:
double funcion(int x);
};
double Base::funcion(int x)
{
//Algoritmo específico para el
//comportamiento de la clase Base
}
double Derivada::funcion(int x)
{
// Nuevo algoritmo para lograr un
// comportamiento distinto
}

Todas los métodos y atributos de la clase
Base se heredan en la clase Derivada,
excepto:
◦ El operador de asignación: Clase::operador=(const
Clase &).
◦ Los constructores y los destructores, incluyendo el
constructor de copia: Clase::(const Clase &).

El programador debe escribir el constructor
de cada clase. Y el constructor de las clases
Derivadas debe llamar al constructor de la
clase Base.

El compilador de C++ crea automáticamente
para toda clase que usted escriba (incluyendo las
clases derivadas):
◦ Un constructor por defecto (que simplemente ocupa la
memoria que necesita el objeto).
◦ Un destructor (que simplemente libera la memoria
ocupada por el objeto).
◦ Un constructor de copia (que copia bit por bit toda la
información del objeto fuente al destino).
◦ Un operador de asignación (que copia bit por bit toda la
información del objeto fuente al destino)

Si usted lo desea, puede evitar que el compilador
cree estas funciones escribiendo sus propias
funciones o situándolas en la parte privada.
Anulándolas por completo.

Concepto previo: Un puntero que apunte a un
objeto de una clase Base, también puede
apuntar a objetos derivados de esa Base.
class Base
{
// etc...
};
class Derivada : public Base
{
// etc...
}
int main()
{
Base *puntero = new Base;
delete puntero;
puntero = 0;
puntero = new Derivada;
// etc...
}



Cada clase tiene una serie de funciones
disponibles. La ligadura de funciones a clases
se hace en tiempo de compilación
Pero también puede hacerse en tiempo de
ejecución (que es mas flexible), usando la
palabra virtual delante de la función.
Ello permite que la decisión de cual función
se va a ejecutar (si la de la clase Base o la de
la clase Derivada) se tome en tiempo de
ejecución.




No es necesario implementar el código de las
funciones virtuales en la clase Base. En este caso
hay que poner = 0 en la declaración de la
función.
A estas funciones que no tienen código, se las
llama funciones virtuales puras.
Las clases que contienen al menos alguna
función virtual pura se las llama clases
abstractas. De ellas no se pueden instanciar
objetos (porque falta el código de las funciones
virtuales puras).
Lo único que se pueda hacer con una clase
abstracta es heredar de ella e implementar en la
clase derivada las funciones faltantes.



No es una palabra muy bien escogida (“muchas
formas”). Mas bien quiere decir “muchos
comportamientos” o “muchas implementaciones”
Empleamos polimorfismo cuando de una clase
con funciones virtuales hereda una o varias
derivadas. Al ejecutar la misma función diversos
objetos (de clases derivadas), su comportamiento
depende de la clase concreta a la que pertenezca
el objeto.
El polimorfismo en C++ se consigue usando
herencia con funciones virtuales y un puntero a
clase Base con el que apuntar a distintos objetos
derivados.



No se puede declarar virtual los constructores, ya
que se ejecutan todos, tanto el de la clase
derivada como el de la(s) clase(s) base.
Sin embargo, los destructores si pueden
declararse virtuales. Es mas, si una clase tiene
alguna función virtual, entonces es obligatorio
declarar su destructor como virtual, para evitar
pérdidas de memoria (memory leaks).
Cuando un destructor es virtual, el
comportamiento es al revés que en las demás
funciones: Obliga al programa a ejecutar todos
los destructores desde la clase derivada hasta la
clase base principal.





En C++ una clase puede heredar de varias clases
base. A esto se le llama herencia múltiple.
Sirve para implementar la nueva relación “es
parecido a varias” clases.
Hay que usar esta relación con mucho cuidado,
ya que no se corresponde con la naturaleza del
mundo real.
De hecho, en POO no es conveniente emplear
herencia múltiple.
Solo en algún caso muy especial puede tener
interés en usarla (para reutilizar y “parchear”
código fuente). Pero no es para principiantes ni
para abusar.
¡Gracias por su atención!
Descargar