Jerarquía de Clases: Herencia, Listas de
Inicialización, Composición y Polimorfismo
Tecnología de la Programación
Javier Nieves Acedo
Esta semana es
“la clave”…
… es el
poder de la POO …
… para hacer lo que
queramos…
… incluso algo
mejor…
…
;-)…
… o más
divertido…
… pero usando
jerarquias…
Herencia (1)
 Hay una relación de herencia entre clases
cuando se cumple la frase “es un”:
Vehículo
“ES UN”
Coche
Camión
Moto
Herencia (2)
 Cuando se realiza una herencia
(generalización) se obtienen los atributos y
los métodos miembros de la clase padre.
Clase Atributo Declaración
A
dato a
A a1;
Representación
a
B
dato b
B b1;
b
c
C
dato c
C c1;
Herencia (3)
 Tipos de Herencia:
 Herencia Simple: una clase base o padre
 Herencia Múltiple: más de una clase base o
padre.
 C++ permite ambas.
 Otros lenguajes no soportan la herencia
múltiple pero proveen mecanismos para
simularla.
Herencia (4)
 Las clases hijas no pueden heredar nada que
sea privado (ni atributos ni métodos)
 La solución actual es poner todo a public
 No se trata de la solución óptima
 Como implementarla:
class <nombreClase> : <nombreClaseBase>
{
…
}
Herencia (5)
 Ejemplo (1):
class Persona
{
public:
char * nombre;
char * direccion;
long telefono;
Persona();
~Persona();
Persona PedirDatosPersona(void);
prPersona(void);
};
Herencia (6)
 Ejemplo (2):
class Empleado : Persona
{
public:
int antiguedad;
int categoria;
int TestAntig(void);
};
¿Qué atributos y métodos tiene la clase
Empleado?
Herencia (y 7)
 Los constructores y destructores no se
heredan.
 En la practica:
 No se generan objetos de la clase base.
 Está para proporcionar el formato común.
… la herencia parece
fantástica…
… pero nos estamos
saltando…
… la
encapsulación…
… y la
solución es …
Acceso protected (1)
 No es bueno perder la ocultación de
métodos y atributos privados para hacer una
generalización.
 Existe otro modo de acceso que solo tiene
sentido en relaciones de herencia o
generalización.
 Atributos y Métodos accesibles por las
clases hijas pero no desde fuera.
Acceso protected (2)
 Ejemplo:
class Base1
{
int a; // Privados por defecto. Disponibles
int b; // solo a otros miembros de la
// clase.
protected:
int c; // Disponibles solo a la propia
// clase
int d; // y a las clases derivadas.
public:
int e; // Disponibles a todas las clases y
int f; // y a otras partes del programa.
};
Acceso protected (3)
 Las clases base no suelen tener miembros
privados
 Las clases hijas necesitan acceder a sus elementos
 La herencia se puede obtener con diferente tipo de
acceso:
 Public (lo habitual): todo se hereda tal y como se
ha indicado
 Private (por defecto): lo heredado pasa a private
 Protected: protected y public pasa a protected
Acceso protected (y 4)
 La plantilla para la clase queda de la
siguiente forma:
class <nombre> : <acceso> <nombre>
{
…
};
… ahora los constructores
son más importantes …
Constructores y Herencia (1)
 Los constructores no se heredan
 Hay que estudiar su funcionamiento
 Hasta ahora, pasos del constructor:
 Crea el objeto, reserva espacio y conecta
nombre y memoria
 Ejecuta instrucciones
 Pero esta forma puede dar problemas…
Constructores y Herencia (y 2)
class Stock
{
char nombre[20];
int cantidad;
float precio;
const int cantidadInicial;
public:
Stock(char *, int, float, int);
};
Stock::Stock(char * N, int C, float P, int I)
{
strcpy(nombre, N);
cantidad = C;
precio = P;
cantidadInicial = I; // ¡No puede!
}
… y volvemos a algo
conocido …
Listas de Inicialización (1)
 Son la solución al problema
 Ejemplo:
Stock::Stock(char * N, int C, float P, int I) :
cantidad(C), precio(P),
cantidadInicial(I)
{
strcpy(nombre, N);
}
Listas de Inicialización (2)
 En lugar de construir y asignar, construyen
e inicializan a la vez que se construye
 IMPORTANTE:
 El constructor de una clase derivada no puede
construirse hasta que se construya a su padre
Listas de Inicialización (3)
 Si hay mas de una generalización, la clase se
preocupa de crear el padre más cercano
Clase Base
A
construye a su padre
Clase Derivada
B
construye a su padre
Clase Derivada
C
Listas de Inicialización (4)
 El constructor de la clase derivada tiene que
usar listas de inicialización para construir a
su padre.
 Hay un objeto más que llama al constructor
del padre
Listas de Inicialización (5)
 Ejemplo (I):
class Padre
{
protected:
char nombre[20]; int edad;
public:
Padre(char *, int);
~Padre(){ };
};
Padre::Padre(char * N, int E) : edad(E)
{
strcpy(nombre, N);
}
Listas de Inicialización (6)
 Ejemplo (y 2):
class Hijo : public Padre
{
int curso;
public:
Hijo(char *, int, int);
~Hijo(){ };
};
Hijo::Hijo(char * N, int E, int C) : Padre(N, E),
curso(C) { }
Listas de Inicialización (7)
 Ejercicio:
 Jerarquía Vehículo, Moto, Coche, Camión
 Vehículo tiene marca y potencia
 Coche tiene el número de asientos
 Camión tiene tara y carga
 Hacer las clases y los constructores para
toda la estructura de clases usando listas de
inicialización siempre que se pueda.
Listas de Inicialización (8)
 Solución (1):
class Vehiculo
{
protected:
char * marca;
int potencia;
public:
Vehiculo(char *, int);
};
Vehiculo::Vehiculo(char * n, int p) :
potencia(p)
{ strcpy(marca, n);}
Listas de Inicialización (9)
 Solución (2):
class Coche : public Vehiculo
{
protected:
int asientos;
public:
Coche(char *, int, int);
};
Coche::Coche(char * n, int p, int a) :
Vehiculo(n, p), asientos(a)
{}
Listas de Inicialización (10)
 Solución (3):
class Moto : public Vehiculo
{
protected:
...
public:
Moto(char *, int);
};
Moto::Moto(char * n, int p) :
Vehiculo(n, p)
{}
Listas de Inicialización (y 11)
 Solución (y 4):
class Camion: public Vehiculo
{
protected:
int tara, carga;
public:
Camion(char *, int, int, int);
};
Camion::Camion(char * n, int p, int t,
int c) : Vehiculo(n, p), tara(t),
carga(c)
{}
… y los
destructores
…
Destructores y la herencia
 Mientras que los constructores hay que
diseñarlos para que se llamen, los
constructores se realizan las llamadas por
defecto.
 Si se ha codificado un destructor en
cualquier elemento de la jerarquía, el
compilador lo utilizará.
… pero hay otras
relaciones …
Composición (1)
 Hay una relación de composición entre
clases cuando se cumple la frase “tiene un”:
Vehículo
“TIENE UN”
Motor
Chasis
Carrocería
Composición (2)
 Los constructores y destructores son
importantes en las relaciones de
composición:
 No se puede construir un objeto hasta que
están construidos todos los que lo
componen
 La lista de inicialización llevará la llamada a
los constructores de todos los objetos que
componen la clase.
Composición (3)
 Ejemplo:
class Chasis{…};
class Motor{…};
class Carroceria{…};
Class Vehiculo
{
Chasis ch;
Motor m;
Carroceria c;
};
Composición (4)
 Pautas de construcción (1):
 Constructor de la clase recibe todo lo
necesario para crear todas las clases.
 La lista de inicialización es larga porque
llama a todos los constructores de las clases
que componen la clase
 El compilador construye primero los
miembros y luego el contenedor
Composición
(5)
 Pautas de construcción (2):
 No hace falta llamar a los destructores de
todas las clases, lo hace el compilador
 La clase compuesta no puede acceder a los
miembros privados de las clases que la
componen
 Para evitar un paso excesivo de parámetros
se pueden crear los objetos y pasarlos como
parámetros a la clase compuesta
… y otra
mejora más …
Polimorfismo (1)
 Es el uso de métodos de igual nombre en
distintas clases dentro de una misma
jerarquía
A
m()
B
D
C
E
m()
F
m()
G
Polimorfismo (2)
 Para poder hacer las llamadas a funciones o
métodos el compilador realiza un proceso de
enlace.
 Tipos:
 Estático: proceso habitual. El linker será el que
modifique la llamada hacia la dirección adecuada.
 Dinámico: no se puede hacer la traducción hasta
el momento de la ejecución (funciones virtuales).
Polimorfismo (3)
 Regla para funciones virtuales:
 Cuando más de una clase dentro de una familia
contiene funciones del mismo nombre (polimórficas)
y además hay definidos apuntadores a varios objetos
dentro de una familia de clases, si se quiere permitir
el enlace dinámico, hay que utilizar funciones
virtuales
 En una familia de clases se puede definir un
apuntador a la clase base y apuntar a
cualquier clase derivada.
Polimorfismo (4)
 Sintaxis:
class Edificio
{
protected:
int metros;
char direccion[20];
public:
Edificio(int, char *);
virtual void VerDatos(void);
};
Polimorfismo (5)
 En un momento dado se puede cancelar el
enlace dinámico.
class Edificio;
class Torre;
Torre * t1 = new Torre();
t1->Edificio::VerDatos();
Polimorfismo (6)
 Función virtual pura: no tiene
implementación en la clase base.
 Sintaxis:
virtual listar() = 0;
 ¿Son iguales estas funciones?
virtual int listar(int n){}
virtual int listar(int n) = 0;
NO
Polimorfismo (y 7)
 Si una clase tiene una función virtual pura
se transforma en una clase abstracta.
 No se pueden construir objetos de una
clase abstracta.
 Se utilizan para definir conceptos
abstractos.
 Se diseñan para ser heredadas.
… y ahora a
trabajar …
Ejercicio
 Generar la siguiente jerarquía:
ANIMAL
PERRO
FELINO
GATO
AVE
LINCE
CANARIO
PALOMA
 Animal no puede ser instanciada.
 Todas los objetos tienen un método que dice su
nombre (se guarda como atributo).
 Todas los objetos tienen un método que dicen que
sonido hacen (no se guarda como atributo).
… y ahora somos unos animales
de la programación.
Jerarquía de Clases: Herencia, Listas de
Inicialización, Composición y Polimorfismo
Tecnología de la Programación
Javier Nieves Acedo
Descargar

Sobrecarga de Funciones y Operadores