martes, 3 de agosto de 2010

Unidad V Herencia

Unidad V
Herencia


5.1 Importancia de la herencia en la POO


La herencia es la propiedad que permite a los objetos ser construidos a partir de otros objetos. Dicho de otro modo, la capacidad de un objeto para utilizar las estructuras de datos y los métodos previstos en antepasados o ascendientes. El objetivo final es la reutilización, es decir, reutilizar código anteriormente ya desarrollado.


La herencia supone una clase base y una jerarquía de clases que contienen las clases derivadas de la clase base.


Una clase hereda sus características (datos y funciones) de otra clase.


La herencia es un mecanismo potente para tratar con la evolución natural de un sistema y con modificación incremental.


5.2 Jerarquía de herencia


Existen dos tipos de herencia: simple y múltiple.


5.2.1 Conceptos de herencia simple y múltiple.

La herencia simple permite que una clase herede las propiedades de su superclase en una cadena jerárquica. En esta jerarquía cada clase tiene como máximo una solo superclase.

La herencia múltiple es la propiedad de una clase de poder tener más de un ascendiente inmediato, o lo que es igual, adquirir datos y métodos de más de una clase.

5.2.2 Principios generales de diseño de jerarquías

Incluso las jerarquías de clases bien diseñadas necesitan evolucionar con el tiempo. Las opciones iniciales que elija en el momento de diseñar una jerarquía de clases pueden simplificar su trabajo posteriormente.



En la siguiente lista se incluyen sugerencias para simplificar la extensión de las jerarquías de clases:


• Las jerarquías se definen desde lo general a lo específico. Defina las clases en cada nivel de una jerarquía de herencia de la forma más genérica posible. Las clases derivadas pueden heredar, reutilizar y extender métodos de clases base. Por ejemplo, suponga que está diseñando una jerarquía de clases que modela el hardware del equipo. Al comenzar a modelar los dispositivos de salida, podría definir clases denominadas Display, Printer y File. Después podría definir las clases que implementan los métodos definidos en las clases base. Por ejemplo, la clase LCD Display puede haber derivado de Display e implementar un método denominado Enter Power Save Mode.



• Defina los tipos de datos y el almacenamiento con generosidad a fin de evitar cambios difíciles posteriormente. Por ejemplo, podría considerar el uso de una variable de tipo Long aunque los datos actuales sólo requieran una variable estándar Integer.



• Exponga sólo los elementos que las clases derivadas necesiten. Los campos y métodos Private reducen los conflictos de denominación y protegen a otros usuarios del uso de elementos que pueden necesitar cambios posteriormente.



• Los miembros que sólo sean necesarios para las clases derivadas deben marcarse como Protected. Esto garantiza que sólo las clases derivadas dependen de estos miembros y facilita la actualización de estos miembros durante el desarrollo.



• Asegúrese de que los métodos de clase base no dependen de miembros Overridable cuya funcionalidad pueden cambiar las clases herederas. Existen dos tipos de jerarquizar: La primera consiste en derivar una clase de otra, es el caso mas habitual y se llama “Herencia”. La otra consiste en encapsular un objeto como miembro de otro, a esta se le llama “Encapsulamiento”.



La unica regla que tenemos que tener en mente es: Si un objeto se relaciona con otro de la forma “es un” hay que usar la herencia publica. Si por el contrario la relacion se describe mejor de la forma “tiene un” hay que usar el encapsulamiento.



Una forma sencilla de ver la diferencia entre los dos tipos es con este ejemplo:



• La clase C Mercedes? “es un” tipo de la clase C Car?


• La clase C Mercedes “tiene un” tipo de case C Volante?



5.2.3 Especificadores de acceso a jerarquía de clases


C++ utiliza especificadores de acceso para permitir controlar a una clase el acceso a las variables de datos de esa clase. Los especificadores de acceso permiten acceder a algunos miembros de la clase y restringir el acceso a otros. Hay tres especificadores de acceso en C++: public, private y protected. Cuando usted declara público ( public) un miembro de una clase, usted permite el acceso a tal miembro desde dentro y fuera de la clase. Los miembros de datos que son declarados protegidos ( protected ) son únicamente accesibles por funciones miembro de la clase, pero no se pueden acceder a ellos desde otras clases. Cuando un miembro de una clase es declarado privado ( private ) es ináccesible no sólo desde otras clases y otras partes del programa, sino también desde sus clases derivadas. Las clases derivadas se explicara posteriormente.


Miremos el siguiente programa de ejemplo. Se compone de tres partes: la primera una declaración de una clase llamada Empleado:
class Empleado

{

private:

char* m_nombre;

char* m_departamento;

char* m_posicion;

long m_salario;

public:

void ImprimirInfo();

void SetNombre( char* nombre ) { m_nombre = nombre }

void SetDepartamento( char * departamento) { m_departamento = departamento }

void SetPosicion ( char* posicion ) { m_posicion = posicion }

void SetSalario ( long salario ) { m_salario = salario }

const char* GetNombre( ){ return m_nombre }

const char* GetDepartamento( ){ return m_departamento }

const char* GetPosicion( ){ return m_posicion }

const char* GetSalario( ){ return m_salario }

};


Las funciones SetNombre, SetDepartamento, setPosicion, setSalario, GetNombre, GetDepartamento, GetPosicion y GetSalario se denominan funciones intercaladas, que son funciones que se declaran en una sola línea.


Las variables de miembro son declaradas privadas para que funciones de miembro de otras funciones no tengan acceso a ellas sino a travez de la correspondiente funcion Get o Set.


Las funciones de miembro si son declaradas públicas de tal modo que se pueda acceder a ellas desde otras funciones.


5.3 Definición de una clase base

Cuando pensamos en una clase como un tipo, asumimos que los programas crearán objetos de ese tipo. Sin embargo, hay casos en que es útil definir clases para las cuales no se desea instanciar objetos. Tales clases son llamadas clases abstractas. Debido a que normalmente son utilizadas como base en jerarquías de clases, nos referiremos a ellas como clases base abstractas.


Las clases abstractas no sirven para instanciar objetos porque están incompletas, siendo sus clases derivadas las que deberán definir las partes faltantes.



El propósito de una clase abstracta es proveer una clase base apropiada desde la cual otras clases hereden.



Las clases desde las cuales se pueden instanciar objetos se llaman clases concretas. Tales clases proveen implementaciones de cada método o propiedad que definen.



Las clases abstractas normalmente contienen uno o más métodos o propiedades abstractas, las cuales no proveen implementación. Las clases derivadas deben reemplazar los métodos abstractos heredados para permitir la instanciación de objetos.



Para definir una clase abstracta se utiliza la palabra clave abstract (que también sirve para definir métodos y propiedades abstractos).



Ejemplo :
// Clase abstracta con métodos abstractos;
abstract class Figura{
protected int x, y ;
public abstract double daPerímetro( ) ; // Declaración de un método abstracto.
public abstract double daArea( ) ; // Declaración de un método abstracto.
}



5.4 Definición de una clase derivada

La clase derivada se crea a partir de otra ya existente, denominada clase base.

La declaración de derivación de clases debe incluir el nombre de la clase base de la que se deriva y el especificador de acceso que indica el tipo de herencia (publica, privada y protegida). La primera línea de cada declaración debe incluir el formato siguiente:

class nombre_clase : tipo_herencia nombre_clase_base
{
// tipos de miembros
};

5.4.1 Constructores y destructores de una clase derivada


Una clase derivada es una especialización de una clase base. En consecuencia el constructor de la clase base debe ser llamado para crear un objeto de la clase base antes de que el constructor de la clase derivada realice su tarea.

Reglas

1. Los constructores de las clases base se invocan antes del constructor de la clase derivada; los constructores de la clase base se invocan en la secuencia en que están especificados.
2. Si una clase base es, a su vez, una clase derivada, sus constructores se invocan también en secuencia: constructor base, constructor derivada.
3. Los constructores no se heredan, aunque los constructores por defecto y de copia, se generan si se requiere.

Sintaxis del constructor de una clase derivada:

nombre_clase_derivada:: nombre_clase_derivada(parámetros):nombre_clase_base(parámetros), listas_inicializacion _miembrosdato
{
// Cuerpo constructor de la clase derivada

};


El constructor de la clase derivada tiene que realizar dos tareas:
1. Inicializar el objeto base
2. Inicializar todos los miembros dato

La clase derivada tiene un constructor inicializador, que llama a uno o más constructores de la clase base. El inicializador aparece inmediatamente después de los parámetros del constructor de la clase derivada y está precedido por dos puntos (:).

Los destructores son mucho mas fáciles de tratar que los constructores. Los destructores no se heredan, aunque se genera un destructor por defecto si se requiere. Un destructor normalmente sólo se utiliza cuando un constructor correspondiente ha asignado espacio de memoria que debe ser liberado. Los destructores se manejan como los constructores excepto que todo se hace en orden inverso. Esto significa que el destructor de la ultima clase derivada se ejecuta primero.

5.4.2 Conversión implícita de objetos de clase derivada a objetos de clase base

La programación orientada a objetos extiende los tipos abstractos de datos permitiendo relaciones tipo-subtipo. Esto es alcanzado a través de un mecanismo denominado herencia. Más que reimplementar características compartidas, una clase puede heredar datos y funciones miembros de otras clases. En C++, este mecanismo es implementado a través de la derivación de clases.

Especificación de derivación

// clases que sirven como clases base

class Animal{...};

class EnExtincion{...};

class Carnivoro{...};

class Herbivoro{...};

// Derivación simple

class Oso:public Animal { ...};

// Derivaciones múltiples

class Gato : public Animal, Carnivoro {...};

class Panda : private EnExtincion, public Oso, private Herbivoro {...};

Si se omite un atributo de público,privado o protegido a la clase base, esta es manipulada como privada. En el caso de la clase gato, Carnivoro es una clase base privada.

La sintaxis para definir clases bases es igual a definir clases ordinarias, con las siguientes dos excepciones:

  • Miembros que se quiere sean heredados por clases derivadas, pero no públicos son declarados miembros protegidos.

  • Funciones miembro cuya implementación depende de detalles de representación de subsecuentes derivaciones y que son desconocidas al tiempo del diseño de la clase base son declaradas como funciones virtuales.


El acceso desde clases derivadas a miembros heredados es como si fueran miembros propios (depende del tipo de herencia realizada). Tambien puede usarse el class scope operator es decir, para acceder al miembro A de la clase ClassA desde el objeto Obj de la clase ClassB sería Obj.ClassA::A o bien Obj.A.

Clases bases públicas y derivadas


Los miembros heredados de una clase base pública mantienen su nivel de acceso dentro de la clase derivada. En general, en una jerarquía de derivación pública, cada clase derivada subsecuentemente tiene acceso combinado a miembros públicos y protegidos de las clases bases previas en una rama. Los miembros públicos y protegidos heredados a través de una derivación protegida, se vuelen miembros protegidos de la clase derivada. Los miembros públicos y protegidos heredados a traves de una derivación privada se vuelven miembros privados de la clase derivada. Esto lleva a:


  • Los miembros públicos de una clase base no pueden ser accedidos a través de un objeto de una clase derivada.

  • Los miembros públicos y protegidos de la clase base ya no seran más visibles en subsecuentes derivaciones.


Conversiones estándar bajo derivación


Existen 4 conversiones predefinidas que son aplicables entre una clase derivada y su clase base pública:


  • Un objeto de la clase derivada se convierte en forma implícita en un objeto de la clase base pública.

  • Una referencia a una clase derivada se convierte implícitamente en una referencia a la clase base pública.

  • Un puntero a la clase derivada se convertirá implícitamente en un puntero a la clase base.

  • Un puntero a un miembro de una clase base se convertirá en forma implícita en un puntero a miembro de la clase derivada.


5.5 Herencia múltiple

Es un tipo de herencia en la que una clase hereda el estado (estructura) y el comportamiento de más de una clase base.

La herencia múltiple puede simplificar los programas y proporcionar soluciones para resolver problemas difíciles.

La herencia múltiple plantea diferentes problemas tales como la ambigüedad por le uso de nombres idénticos en diferentes clases base, y la dominación o preponderancia de funciones o datos.


Referencias:

No hay comentarios:

Publicar un comentario