UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE...

84
1 UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMAS DOSSIER PROGRAMACION III DOCENTE LIC. PATRICIA PALACIOS ZULETA PARALELO A2 I I- 2011

Transcript of UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE...

Page 1: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

1

UNIVERSIDAD SALESIANA DE BOLIVIA

INGENIERIA DE SISTEMAS

DOSSIER

PROGRAMACION III

DOCENTE LIC. PATRICIA PALACIOS ZULETA PARALELO A2

I I- 2011

Page 2: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

2

INDICE Pag. UNIDAD I CONCEPTOS FUNDAMENTALES DE PROGRAMCION ORIENTADA A OBJETOS. Introducción……………………………………………………………………………………….. Programación Orientada a Objetos………………………………………………...…………… Breve historia de la P.O.O.................................................................................................... Conceptos básicos............................................................................................................... Definición de objeto.............................................................................................................. Herencia................................................................................................................................ Abstracción............................................................................................................................ Encapsulación....................................................................................................................... Polimorfismo.......................................................................................................................... Función Amiga....................................................................................................................... UNIDAD II PRINCIPIOS DE PROGRAMACION ORIENTADA A OBJETOS CON C++ Y JAVA Introducción.......................................................................................................................... Historia y características del C++.......................................................................................... Características del C++......................................................................................................... Abstracción de datos............................................................................................................ Clases y objetos en C++........................................................................................................ Envió de mensajes............................................................................................................... Encapsulamiento.................................................................................................................. Constructores y destructores................................................................................................ Historia del Java.................................................................................................................... Ejercicios................................................................................................................................

UNIDAD III SOBRECARGA Sobrecarga............................................................................................................................ Sobrecarga de funciones....................................................................................................... Sobrecarga de operadores.................................................................................................... Ejercicios................................................................................................................................ UNIDAD IV HERENCIA Introducción........................................................................................................................... Herencia................................................................................................................................ Herencia Simple..................................................................................................................... Control de acceso a la Clase base......................................................................................... Constructores en Herencia..................................................................................................... Herencia Múltiple....................................................................................................................

1 3 5 7 10 13 16 16 17 17 18 18 19 20 22 22 24 27 28 32 33 3336 39 42 42 42 45 50 52

Page 3: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

3

UNIDAD V FUNCIONES VIRTUALES Y POLIMORFISMO Introducción......................................................................................................................... Ligadura.............................................................................................................................. Tipos de Ligadura................................................................................................................ Funciones Virtuales............................................................................................................. Polimorfismo........................................................................................................................ Ejemplos.............................................................................................................................. UNIDAD VI PLANTILLAS Y TRATAMIENTO DE EXCEPCIONES Introducción.......................................................................................................................... Plantillas................................................................................................................................ Concepto de Excepciones..................................................................................................... Lanzamiento de excepciones................................................................................................

LECTURAS COMPLEMENTARIAS BIBLIOGRAFIA

57 57 60 60 66 67 68 68 72 74

79 80

Page 4: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

4

I OBJETIVOS DE LA MATERIA

GENERAL

El objetivo principal radica principalmente en permitir conocer, aplicar y desarrollar el paradigma

de POO en el desarrollo de aplicaciones profesionales para la resolución de problemas, utilizando

las herramientas de software, lo cual facilitara la inserción de los profesionales en el desarrollo de

software.

ESPECÍFICOS

Conocer algunas técnicas de ingeniería de software usadas en el diseño y la

implementación de programas en el lenguaje de programación C++ y Java.

Aplicar las estructuras de datos más convenientes para solucionar problemas específicos.

II COMPETENCIA DE LA MATERIA

El objetivo de la asignatura es formar al alumno en los conceptos de la Programación Orientada a

Objetos, haciendo especial énfasis en sus aspectos más prácticos. Esto supone que a la

finalización del curso el alumno estará plenamente capacitado para desarrollar un programa

siguiendo el paradigma orientado a objetos como alternativa a la programación procedimental.

Para ello se introducirá al alumno en el lenguaje de programación más ampliamente usado el

lenguaje de programación C++ y Java.

COMPETENCIAS

De forma más esquemática, las competencias que adquirió el alumno son las siguientes:

1. Conocer y manipular con destreza el concepto de abstracción de datos.

2. Diseñar, especificar e implantar tipos de datos abstractos.

3. Dominar con destreza los paradigmas de la orientación por objetos.

4. Conocer técnicas para la especificación, diagramación y desarrollo de sistemas

Page 5: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

5

TEMA I

TEMA No 1

INTRODUCCIÓN A LA PROGRAMACIÓN ORIENTADA A OBJETOS

Introducción.-

La programación orientada a objetos (o POO), es un paradigma de desarrollo definido y utilizado

por una serie de lenguajes de programación que busca afrontar el desarrollo de aplicaciones

desde un enfoque distinto al estructurado.

Toda la historia de los lenguajes de programación se ha desarrollado en base a una sola idea

conductora: hacer que la tarea de realizar programas para ordenadores sea cada vez lo más

simple, flexible y portable posible.

La POO supone, no solo un nuevo paso hacia ese fin, sino que además, a nuestro juicio, es el

más importante acaecido hasta el momento. Como nos comenta Eckel: "A medida que se van

desarrollando los lenguajes, se va desarrollando también la posibilidad de resolver problemas

cada vez más complejos. En la evolución de cada lenguaje, llega un momento en el que los

programadores comienzan a tener dificultades a la hora de manejar programas que sean de un

cierto tamaño y sofisticación."

Esta evolución en los lenguajes, ha venido impulsada por dos motores bien distintos:

Los avances tecnológicos

Los avances conceptuales (de planteamiento)

Los avances en cuanto a enfoque de la programación

Evolución en cuanto a la conceptualización.-

El primer avance en metodología de programación, vino con la Programación Estructurada (en

este concepto vamos a incluir el propio y el de técnicas de Programación con Funciones

también llamado procedural, ya que ambos se hallan íntimamente relacionados, no creemos, que

se pueda concebir la programación estructurada sin el uso masivo de funciones).

Page 6: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

6

La programación en ensamblador es lineal, es decir, las instrucciones se ejecutan en el mismo

orden en que las escribimos. Podemos, sin embargo, alterar este orden haciendo saltos desde

una instrucción a otro lugar del programa distinto a la instrucción que le sigue a la que se estaba

procesando.

Programación lineal.-

Cada línea de programa debe ir precedida de un identificador (una etiqueta) para poder

referenciarla, para este ejemplo hemos utilizado números, aunque podría utilizarse cualquier otro

identificador:

1. Hacer una variable igual a 0

2. Sumar 1 a esa variable

3. Mostrar la variable

4. Si la variable es 100 -> terminar, Si_no -> saltar a 1.:

Programación estructurada.-

1. Hacer una variable igual a 0

2. Mientras que sea menor que 100 -> sumar 1 y mostrarla

Lo importante aquí, es que cuando escribimos un programa usando las técnicas de programación

estructurada, los saltos están altamente desaconsejados, por no decir prohibidos; en cambio en

BASIC, por ejemplo, son muy frecuentes (todos conocemos el prolífico GOTO <nLínea>), lo que

no es nada conveniente si queremos entender algo que escribimos hace tres meses de forma

rápida y clara.

Para evitar esto, junto con la programación estructurada aparece un concepto que nos permite

abarcar programas más amplios con menor esfuerzo: el de función.

La idea es muy simple: muchas veces realizo procesos que se repiten y en los que sólo cambia

algún factor, si trato ese proceso como un subprograma al que llamo cada vez que lo necesito, y

cada vez que lo llamo puedo cambiar ese factor, estaré reduciendo el margen de error, al reducir

el número de líneas que necesito en mi programa, ya que no tengo que repetir todas esas líneas

cada vez que quiera realizar el proceso, con una sola línea de llamada al subprograma será

Page 7: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

7

suficiente; además, de haber algún fallo en este proceso el error queda circunscrito al trozo de

código de la función.

Así, las funciones podemos entenderlas como unas cajas negras, que reciben y devuelven

valores. Solo tengo que programarlas una vez, las puedo probar por separado y comprobar que

funcionan correctamente, una vez terminadas puedo olvidarme de cómo las hice y usarlas

siempre que quiera.

Programación Orientada al Objeto.-

Por último, llegamos al más reciente avance, la POO, que nos ofrece mucho mayor dominio sobre

el programa liberándonos aún más de su control. Hasta ahora, el control del programa era tarea

del programador. El programador tenía que controlar y mantener en su mente cada proceso que

se realizaba y los efectos colaterales que pudieran surgir entre distintos procesos, lo que

llamamos colisiones. En POO, el programa se controla a sí mismo y la mente del programador se

libera enormemente pudiendo realizar aplicaciones mucho más complejas al exigir menor esfuerzo

de atención, ya que los objetos son entidades autónomas que se controlan (si han sido creados

correctamente) a sí mismos. Esto es posible principalmente porque los objetos nos impiden

mezclar sus datos con otros métodos distintos a los suyos.

En programación estructurada, una función trabaja sobre unos datos, y no debería modificar datos

que no le corresponde hacer, pero de eso tiene que encargarse el programador, en POO es el

propio sistema de trabajo el que impide que esto ocurra. Además, la re - usabilidad del código

escrito es mucho mayor que con el uso de funciones, y las portabilidad es también mayor.

¿Qué es la POO?

Comenzaremos dando una definición por exclusión de lo que es la POO, es decir, vamos a decir

primero qué no es la POO, para, luego, intentar dar una definición de lo que es.

LA POO no es:

No es un lenguaje. De hecho las técnicas de POO pueden utilizarse en cualquier lenguaje

conocido y los que están por venir, aunque estos últimos, al menos en los próximos años, incluirán

facilidades para el manejo de objetos. Desde luego, que en los lenguajes que prevén el uso de

objetos la implementación de las técnicas de POO resulta mucho más fácil y provechosa que los

Page 8: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

8

otros. Pero del mismo modo a lo comentado en el punto anterior, se pueden utilizar estos

lenguajes sin que los programas resultantes tengan nada que ver con la POO

Como ya hemos comentado, la POO son un conjunto de técnicas que nos permiten incrementar

enormemente nuestro proceso de producción de software; aumentando drásticamente nuestra

productividad por un lado y permitiéndonos abordar proyectos de mucha mayor envergadura por

otros.

Usando estas técnicas, nos aseguramos la re-usabilidad de nuestro código, es decir, los objetos

que hoy escribimos, si están bien escritos, nos servirán para "siempre".

Hasta aquí, no hay ninguna diferencia con las funciones, una vez escritas, estas nos sirven

siempre. Pero es que, y esto sí que es innovador, con POO podemos re-usar ciertos

comportamientos de un objeto, ocultando aquellos otros que no nos sirven, o redefinirlos para que

los objetos se comporten de acuerdo a las nuevas necesidades.

Veamos un ejemplo simple: si tenemos un coche y queremos que sea más

rápido, no construimos un coche nuevo; simplemente le cambiamos el carburador por otro más

potente, cambiamos las ruedas por otras más anchas para conseguir mayor estabilidad y le

añadimos un sistema turbo. Pero seguimos usando todas las otras piezas de nuestro coche.

Desde el punto de vista de la POO ¿Qué hemos hecho?

Hemos modificado dos cualidades de nuestro objeto (métodos): el carburador y las ruedas.

Hemos añadido un método nuevo: el sistema turbo.

En programación tradicional, nos hubiésemos visto obligados a construir un coche nuevo por

completo.

Dicho en términos de POO, si queremos construir un objeto que comparte ciertas cualidades con

otro que ya tenemos creado, no tenemos que volver a crearlo desde el principio; simplemente,

decimos qué queremos usar del antiguo en el nuevo y qué nuevas características tiene nuestro

nuevo objeto.

Page 9: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

9

Aún hay más, con POO podemos incorporar objetos que otros programadores han construido en

nuestros programas, de igual modo a como vamos a una Ferretería y compramos piezas de

madera para ensamblarlas y montar una estantería o una mesa. Pero, es que además podemos

modificar los comportamientos de los objetos construidos por otros programadores sin tener que

saber cómo los han construido ellos.

Breve historia de la POO.-

Los conceptos de clase y herencia fueron implementados por vez primera en el lenguaje Simula

67 (el cual no es sino una extensión de otro más antiguo, llamado Algol 60), este fue diseñado en

1967 por Ole-Johan Dhal y Krysten Nygaard en la Universidad de Oslo y el Centro de

Computación Noruego (Norsk Regnesentral).

La historia de Simula, que es como se le llama coloquialmente, es tan frecuente como

desafortunada. Fue diseñado como un lenguaje de propósito general y pasó por el mundo de la

informática sin pena ni gloria durante años. Fue mucho después, con la aparición de otros

lenguajes que se basaban en estos innovadores conceptos (Smalltalk y sobretodo C++), cuando

se le reconoció a los creadores de Simula su gran mérito. Sin embargo, Simula sigue sin usarse

porque estos conceptos han sido ampliados y han aparecido otros nuevos que le dan mayor

potencia y flexibilidad a los conceptos originales de clase y herencia, conformando lo que hoy

entendemos por Programación Orientada al Objeto.

Aunque Simula fue el padre de todo este revuelo, ha sido Smalltalk quién dio el paso definitivo y

es éste el que debemos considerar como el primer lenguaje de programación orientado a objetos.

Smalltalk fue diseñado (cómo no) en el Palo Alto Research Center (PARC) de Xeros

Corporation's, en California.

El último gran paso, a nuestro juicio, lo dio Bjarne Stroustrup con la creación del C++, quizás el

lenguaje de programación orientado a objetos más usado actualmente. Este, fue definido en 1986

por su autor en un libro llamado The C++ Programming Language, de cita y referencia obligadas

cuando se habla de POO.

Page 10: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

10

Fig. 1 Evolución de los lenguajes Orientados a objetos

Llegados a este punto se hace necesario aclarar que los lenguajes de POO, podemos clasificarlos

en puros e híbridos. Diremos que un lenguaje es POO puro, cuando se ajusta completamente a

los principios que esta técnica propone y contempla la posibilidad de trabajar exclusivamente con

clases. Diremos que un lenguaje es híbrido de POO y algún otro, cuando ese lenguaje, que

normalmente existía antes de la aparición de la POO, incorpora en mayor o menor medida

facilidades para trabajar con clases.

De este modo, C++ es un lenguaje POO híbrido. De hecho, C++ no incorpora todas las

características de un lenguaje POO, y no lo hace principalmente, porque es un lenguaje compilado

y ello impide que se resuelvan ciertas referencias en tiempo de compilación necesarias para dotar

a las clases de algunas de sus cualidades puramente POO (a menos que se le dote de un motor

POO interno, el cual tiene en parte).

C ha pasado a llamarse C++. Es la mejor implementación POO sobre un lenguaje compilado

existente en este momento. Su punto más conflictivo no obstante no su total implementación

POO, sino el hecho de permitir herencia múltiple, lo que, según los teóricos, no es muy

aconsejable.

Page 11: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

11

Pascal ha sido reciclado por Borland, pasando a llamarse Delphi. Tiene una implementación POO

bastante buena, pero al no permitir sobrecarga de funciones pierde una de las características de

POO: Polimorfismo.

Basic ha pasado a llamarse Visual Basic en manos de Microsoft. Este, aun siendo uno de los

lenguajes más famosos del momento, no es un lenguaje POO completo, ya que no incluye la

característica más importante de la POO: la Herencia.

Java es la estrella de los últimos años: es pura POO, impecablemente concebido e implementado

por Sun, no dispone de sobrecarga de operadores, cualidad que de los citados aquí sólo dispone

C++, No dispone de herencia múltiple como C++.

Conceptos básicos.-

Estudiaremos los conceptos de Clase, Objeto, Herencia, Encapsulación, Polimorfismo, Funciones

amigas, Mensaje, Métodos, Datos y Abstracción. Estas son las ideas más básicas que todo aquel

que trabaja en POO debe comprender y manejar constantemente; es por lo tanto de suma

importancia que los entienda claramente.

Definición de Clase.-

Una clase, es simplemente una abstracción que hacemos de nuestra experiencia sensible. El ser

humano tiende a agrupar seres o cosas “objetos” con características similares en grupos “clases”.

Así, aun cuando existen por ejemplo multitud de vasos diferentes, podemos reconocer un vaso en

cuanto lo vemos, incluso aun cuando ese modelo concreto de vaso no lo hayamos visto nunca. El

concepto de vaso es una abstracción de nuestra experiencia sensible.

Quizás el ejemplo más claro para exponer esto lo tengamos en las taxonomías; los biólogos han

dividido a todo ser (vivo o inerte) sobre la tierra en distintas clases.

Tomemos como ejemplo una pequeña porción del inmenso árbol taxonómico:

Page 12: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

12

Ellos, llaman a cada una de estas parcelas reino, tipo, clase, especie, orden, familia, género, etc.;

sin embargo, nosotros a todas las llamaremos del mismo modo: clase. Así, hablaremos de la

clase animal, clase vegetal y clase mineral, o de la clase félidos y de las clases leo (león) y tigris

(tigre).

Cada clase posee unas cualidades que la diferencian de las otras. Así, por ejemplo, los vegetales

se diferencian de los minerales “entre otras muchas cosas” en que los primeros son seres vivos y

los minerales no. De los animales se diferencian en que las plantas son capaces de sintetizar

clorofila a partir de la luz solar y los animales no.

Situémonos en la clase felinos (felis), aquí tenemos varias subclases (géneros en palabras de los

biólogos): león, tigre, pantera, gato, etc. cada una de estas subclases, tienen características

comunes (por ello los identificamos a todos ellos como felinos) y características diferenciadoras

(por ello distinguimos a un león de una pantera), sin embargo, ni el león ni la pantera en abstracto

existen, existen leones y panteras particulares, pero hemos realizado una abstracción de esos

rasgos comunes a todos los elementos de una clase, para llegar al concepto de león, o de

pantera, o de felino.

La clase león se diferencia de la clase pantera en el color de la piel, y comparte ciertos atributos

con el resto de los felinos “uñas retráctiles por ejemplo” que lo diferencian del resto de los

animales. Pero la clase león, también hereda de las clases superiores ciertas cualidades: columna

Page 13: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

13

vertebral (de la clase vertebrados) y es alimentado en su infancia por leche materna (de la clase

mamíferos).

Vemos cómo las clases superiores son más generales que las inferiores y cómo, al ir bajando por

este árbol, vamos definiendo cada vez más (dotando de más cualidades) a las nuevas clases.

Hay cualidades que ciertas clases comparten con otras, pero no son exactamente iguales en las

dos clases. Por ejemplo, la clase hombre, también deriva de la clase vertebrado, por lo que ambos

poseen columna vertebral, sin embrago, mientras que en la clase hombre se halla en posición

vertical, en la clase león la columna vertebral está en posición horizontal.

En POO existe otro concepto muy importante asociado al de clase, el de "clase abstracta". Una

clase abstracta es aquella que construimos para derivar de ella otras clases, pero de la que no se

puede instanciar. Por ejemplo, la clase mamífero, no existe como tal en la naturaleza, no existe

ningún ser que sea tan solo mamífero (no hay ninguna instanciación directa de esa clase), existen

humanos, gatos, conejos, etc. Todos ellos son mamíferos, pero no existe un animal que sea solo

mamífero.

Del mismo modo, la clase que se halla al inicio de la jerarquía de clases, normalmente es creada

sólo para que contenga aquellos datos y métodos comunes a todas las clases que de ella derivan:

Son clases abstractas. En árboles complejos de jerarquías de clases, suele haber más de una

clase abstracta.

Este es un concepto muy importante: el de "clase abstracta". Como hemos dicho, una clase

abstracta es aquella que construimos para derivar de ella otras clases, pero de la que no se puede

instanciar. Por ejemplo, la clase mamífero, no existe como tal en la naturaleza, no existe ningún

ser que sea tan solo mamífero (no hay ninguna instanciación directa de esa clase), existen

humanos, gatos, conejos, etc. Todos ellos son mamíferos, pero no existe un animal que sea solo

mamífero.

Todos estos hombres comparten las características de la clase hombre, pero son diferentes entre

sí, en estatura, pigmentación de la piel, color de ojos, complexión, etc. A cada uno de los hombres

particulares los llamamos "objetos de la clase hombre". Decimos que son objetos de tipo hombre o

que pertenecen a la clase hombre. Más técnicamente, Patricia Palacios o Leonardo da Vinci son

Page 14: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

14

instanciaciones de la clase hombre; instanciar un objeto de una clase es crear un nuevo elemento

de esa clase, cada niño que nace es una nueva instanciación a la clase hombre.

Definición de Objeto.-

En POO, un objeto es un conjunto de datos y métodos

Fig. 3 Representación visual de un objeto

Los datos son lo que antes hemos llamado características o atributos, los métodos son los

comportamientos que pueden realizar.

Lo importante de un sistema POO es que ambos, datos y métodos están tan intrínsecamente

ligados, que forman una misma unidad conceptual y operacional. En POO, no se pueden desligar

los datos de los métodos de un objeto. Así es como ocurre en el mundo real.

Page 15: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

15

Ejemplo 1:

Tomemos la clase león de la que hablamos antes y veamos cuales serían algunos de sus datos y

de sus métodos.

Ejemplo 2:

Pongamos otro ejemplo algo más próximo a los objetos que se suelen tratar en informática: un

recuadro en la pantalla.

El recuadro pertenecería a una clase a la llamaremos marco. Veamos sus datos y sus métodos.

Page 16: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

16

Ejemplo 3:

Vayamos con otro ejemplo más. Este es para aquellos que ya tienen conocimientos de

informática, y en especial de lenguaje C o Java.

Una clase es un nuevo tipo de dato y un objeto cada una de las asignaciones que hacemos a ese

tipo de dato.

int nEdad = 30;

Hemos creado un objeto que se llama nEdad y pertenece a la clase integer (entero).

Para crear un objeto de la clase marco lo haríamos de forma muy parecida: llamando a la función

creadora de la clase marco:

Marco oCajaMsg := new Marco();

Objeto.-

Cuando se ejecuta un programa orientado a objetos, los objetos están recibiendo, interpretando y

respondiendo a mensajes de otros objetos, Esto marca una clara diferencia con respecto a los

elementos de datos pasivos de los sistemas tradicionales. En la POO un mensaje esta asociado

con un método, de tal forma que cuando un objeto recibe un mensaje la respuesta a este mensaje

es ejecutar el método asociado

Por ejemplo cuando un usuario quiere maximizar una ventana de una aplicación de

Windows, lo hace simplemente es pulsar el botón de la misma que realiza esa acción. Esto

provoca que Windows envié un mensaje a la ventana para indicar que tiene que maximizarse.

Como respuesta a este mensaje se ejecutara el método programado parar ese fin.

Page 17: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

17

Herencia.-

La herencia permite el acceso automático a la información contenida en otra clase. De esta forma

la reutilización de código esta garantizada. Con la herencia todas las clases están clasificadas en

jerarquías estrictas. Cada clase tiene su superclase (la clase superior en la jerarquía), y cada

clase puede tener una o más subclases (las clases inferiores en la jerarquía).

Como todos entendemos lo que es la herencia biológica, continuaremos con nuestro ejemplo

taxonómico del que hablábamos en el apartado anterior.

La clase león, como comentábamos antes, hereda cualidades, métodos, en lenguaje POO de

todas las clases predecesoras padres, en POO y posee métodos propios, diferentes a los del

resto de las clases. Es decir, las clases van especializándose según se avanza en el árbol

taxonómico. Cada vez que creamos una clase heredada de otra (la padre) añadimos métodos a la

clase padre o modificamos alguno de los métodos de la clase padre.

Veamos qué hereda la clase león de sus clases padre:

La clase león hereda todos los métodos de las clases padre y añade métodos nuevos que forman

su clase distinguiéndola del resto de las clases: por ejemplo el color de su piel.

Observemos ahora algo crucial que ya apuntábamos antes: dos subclases distintas, que derivan

de una misma clase padre común, pueden heredar los métodos de la clase padre tal y como estos

han sido definidos en ella, o pueden modificar todos o algunos de estos métodos para adaptarlos

a sus necesidades.

En el ejemplo que exponíamos antes, en la clase león la alimentación es carnívora, mientras que

en la clase hombre, se ha modificado éste dato, siendo su alimentación omnívora.

Page 18: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

18

La herencia como puede intuir, es la cualidad más importante de la POO, ya que le permite

reutilizar todo el código escrito para las superclases re-escribiendo solo aquellas diferencias que

existan entre éstas y las subclases.

Veamos ahora algunos aspectos más técnicos de la herencia:

A la clase heredada se le llama, subclase o clase hija, y a la clase de la que se hereda superclase

o clase padre.

Al heredar, la clase heredada toma directamente el comportamiento de su superclase, pero puesto

que ésta puede derivar de otra, y esta de otra, etc., una clase toma indirectamente el

comportamiento de todas las clases de la rama del árbol de la jerarquía de clases a la que

pertenece.

Se heredan los datos y los métodos, por lo tanto, ambos pueden ser redefinidos en las clases

hijas, aunque lo más común es redefinir métodos y no datos.

Muchas veces las clases especialmente aquellas que se encuentran próximas a la raíz en el árbol

de la jerarquía de clases son abstractas, es decir, sólo existen para proporcionar una base para la

creación de clases más específicas, y por lo tanto no puede instanciarse de ellas; son las clases

virtuales.

Una subclase hereda de su superclase sólo aquellos miembros visibles desde la clase hija y por lo

tanto solo puede redefinir estos.

Una subclase tiene forzosamente que redefinir aquellos métodos que han sido definidos como

abstractos en la clase padre o padres.

Normalmente, como hemos comentado, se redefinen los métodos, aun cuando a veces se hace

necesario redefinir datos de las clases superiores. Al redefinir un método queremos o bien sustituir

el funcionamiento del método de la clase padre o bien ampliarlo.

En el primer caso (sustituirlo) no hay ningún problema, ya que a la clase hija la dotamos con un

método de igual nombre que el método que queremos redefinir en la clase padre y lo

Page 19: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

19

implementamos según las necesidades de la clase hija. De este modo cada vez que se invoque

este método de la clase hija se ejecutará su código, y no el código escrito para el método

homónimo de la clase padre.

Pero si lo que queremos es ampliar el funcionamiento de un método existente en la clase padre (lo

que suele ser lo más habitual), entonces primero tiene que ejecutarse el método de la clase padre,

y después el de la clase hija. Pero como los dos métodos tienen el mismo nombre, se hace

necesario habilitar alguna forma de distinguir cuando nos estamos refiriendo a un método de la

clase hija y cuando al del mismo nombre de la clase padre.

En Java cada clase solo puede tener una superclase, lo que se denomina “Herencia Simple”. En

otros lenguajes orientados a objetos, como C++, las clases pueden tener más una superclase, lo

que se conoce como “Herencia múltiple”. En este caso una clase comparte los métodos y

propiedades de varias clases, Esta característica proporciona un poder enorme a la hora de crear

clases, pero complica excesivamente la programación. La solución a este problema en java son

las interfaces.

Una interfaz es una colección de nombres de métodos, sin incluir sus definiciones, que pueden ser

añadidas a cualquier clase para proporcionar comportamientos adicionales no incluidos en los

métodos propios o heredados.

Por ejemplo una herencia simple puede ser la clase Profesor como un tipo clase derivado de

Persona. La clase profesor es un tipo de (es-un) Persona, al que añade sus propias

características.

Persona

Profesor

Es un

Page 20: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

20

Por ejemplo la clase Persona utilizando herencia múltiple. Como en ella se ilustra, el

GerenteVentas hereda de Gerente y Vendedor; de modo similar, EstudianteTrabajador de

Estudiante.

Abstracción.-

Por medio de la abstracción conseguimos no detenernos en los detalles concretos de las cosas

que no interesen en cada momento, sino generalizar y centrarse en los aspectos que permiten

tener una visión global del problema.

Encapsulación.-

Esta característica permite ver un objeto como una caja negra en la que se ha introducido de

alguna manera toda la información relacionada con dicho objeto. Esto nos permite manipular los

objetos como unidades básicas, permitiendo ocultar su estructura interna.

Persona

Gerente

Empleado

Estudiante

Trabajador

Estudiante

Gerente de

Ventas

Objeto Real Objeto

Abstracto

Page 21: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

21

La abstracción y la Encapsulación están representadas por clases. La clase es una abstracción,

porque en ella se define las propiedades o atributos de determinados conjuntos de objetos con

características comunes, y es una Encapsulación porque constituye una caja negra que encierra

tanto los datos que almacenan cada objeto como los métodos que permiten manipularlos.

Polimorfismo.-

Esta característica permite implementar múltiples formas de un mismo método, dependiendo cada

una de ellas de la clase sobre las que se realice la implementación. Esto hace que se pueda

acceder a una variedad de métodos distintos (todos con el mismo nombre) utilizando exactamente

el mismo medio de acceso.

Page 22: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

22

TEMA II

PRINCIPIOS DE PROGRAMACIÓN ORIENTADA A OBJETOS CON

C++ Y JAVA

Introducción.-

Vivimos en un mundo de objetos, Cada objeto tiene atributos operaciones. Algunos objetos están

más animados que otros. Se puede agrupar los objetos en clases. Ejemplo, una naranja es un

objeto que pertenece a la clase frutas. La POO usa la notación de objeto del mundo real para

desarrollar aplicaciones. La Clase define una categoría de objetos. Cada objeto es una instancia

de una clase.

Historia y características de C++.-

El lenguaje C++ se comenzó a desarrollar en 1980. Su autor fue Bjarne. Stroustrup, también de la

ATT. Al comienzo era una extensión del lenguaje C que fue denominada C with classes. Este

nuevo lenguaje comenzó a ser utilizado fuera de la ATT en 1983. El nombre C++ es también de

ese año, y hace referencia al carácter del operador incremento de C (++). Ante la gran difusión y

éxito que iba obteniendo en el mundo de los programadores, la ATT comenzó a estandarizarlo

internamente en 1987. En 1989 se formó un comité ANSI (seguido algún tiempo después por un

comité ISO) para estandarizarlo a nivel americano e internacional.

En la actualidad, el C++ es un lenguaje versátil, potente y general. Su éxito entre los

programadores profesionales le ha llevado a ocupar el primer puesto como herramienta de

desarrollo de aplicaciones. El C++ mantiene las ventajas del C en cuanto a riqueza de operadores

y expresiones, flexibilidad, concisión y eficiencia. Además, ha eliminado algunas de las dificultades

y limitaciones del C original. La evolución de C++ ha continuado con la aparición de Java, un

lenguaje creado simplificando algunas cosas de C++ y añadiendo otras, que se utiliza para

realizar aplicaciones en Internet.

El C++ es a la vez un lenguaje procedural (orientado a algoritmos) y orientado a objetos. Como

lenguaje procedural se asemeja al C y es compatible con él, aunque ya se ha dicho que presenta

ciertas ventajas (las modificaciones menores, que se verán a continuación). Como lenguaje

orientado a objetos se basa en una filosofía completamente diferente, que exige del

programador un completo cambio de mentalidad. Las características propias de la Programación

Page 23: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

23

Orientada a Objetos de C++ son modificaciones mayores que sí que cambian radicalmente su

naturaleza.

Características del C++.-

Modificaciones Menores.-

Como ya se ha dicho, el C++ contiene varias modificaciones menores sobre el C original.

Normalmente se trata de aumentar la capacidad del lenguaje y la facilidad de programación en un

conjunto de detalles concretos basados en la experiencia de muchos años. Como el ANSI C es

posterior a los primeros compiladores de C++, algunas de estas modificaciones están ya

introducidas en el ANSI C. En cualquier caso, se trata de modificaciones que facilitan el uso del

lenguaje, pero que no cambian su naturaleza.

Hay que indicar que el C++ mantiene compatibilidad casi completa con C, de forma que el viejo

estilo de hacer las cosas en C es también permitido en C++, aunque éste disponga de una mejor

forma de realizar esas tareas.

Cambio en la extensión del nombre de los ficheros.-

El primer cambio que tiene que conocer cualquier programador es que los ficheros fuente de C++

tienen la extensión *.cpp (de C plus plus, que es la forma oral de llamar al lenguaje en inglés), en

lugar de *.c. Esta distinción es muy importante, pues determina ni más ni menos el que se utilice

el compilador de C o el de C++. La utilización de nombres incorrectos en los ficheros puede dar

lugar a errores durante el proceso de compilación.

Comentarios introducidos en el programa.-

En C los comentarios empiezan por los caracteres /* y terminan con los caracteres */. Pueden

comprender varias líneas y estar distribuidos de cualquier forma, pero todo aquello que está entre

el /* (inicio del comentario) y el */ (fin del comentario) es simplemente ignorado por el compilador.

Algunos ejemplos de formato de comentarios son los siguientes:

Page 24: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

24

/* Esto es un comentario simple. */

/* Esto es un comentario más largo,

distribuido en varias líneas. El

texto se suele alinear por la izquierda. */

/**************************************

* Esto es un comentario de varias *

* líneas, encerrado en una caja para *

* llamar la atención. *

**************************************/

En C++ se admite el mismo tipo de comentarios que en C, pero además se considera que son

comentarios todo aquel texto que está desde dos barras consecutivas (//) hasta el fin de la línea.

Las dos barras marcan el comienzo del comentario y el fin de la línea, el final. Si se desea poner

comentarios de varias líneas, hay que colocar la doble barra al comienzo de cada línea. Los

ejemplos anteriores se podrían escribir del siguiente modo:

// Esto es un comentario simple.

// Esto es un comentario más largo,

// distribuido en varias líneas. El

// texto se suele alinear por la izquierda.

//*************************************

// Esto es un comentario de varias *

// líneas, encerrado en una caja para *

// llamar la atención. *

//*************************************

La ventaja de este nuevo método es que no se pueden comentar inadvertidamente varias líneas

de un programa abriendo un indicador de comentario que no se cierre en el lugar adecuado.

Abstracción de datos.-

La abstracción es el proceso en el cual separamos las propiedades más importantes de un objeto,

de las que no lo son. Es decir, por medio de la abstracción definimos las características esenciales

de un objeto del mundo real, los atributos y comportamientos que lo definen como tal, para

después modelarlo en un objeto de software.

Page 25: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

25

En el proceso de abstracción no debemos preocuparnos por la implementación de cada método o

atributo, solamente debemos definirlo de forma general. Por ejemplo, supongamos que deseamos

escribir un programa para representar el sistema solar, por medio de la abstracción, podemos ver

a éste sistema como un conjunto de objetos, algunos de estos objetos son los planetas, que

tienen un estado, dado por sus características físicas, digamos, tamaño, masa, entre otras, estos

objetos tendrán también comportamientos: la translación y la rotación, pueden mencionarse como

los más evidentes.

Visualizar las entidades que deseamos trasladar a nuestros programas, en términos abstractos,

resulta de gran utilidad para un diseño optimo de nuestro software, ya que nos permite

comprender más fácilmente la programación requerida.

En la tecnología orientada a objetos la herramienta principal para soportar la abstracción es la

clase. Podemos definir a una clase como una descripción genérica de un grupo de objetos que

comparten características comunes, dichas características se especificadas en sus atributos y

comportamientos. En otras palabras, una clase es un molde o modelo en donde se especifican las

características que definen a un objeto de manera general, a partir de una clase pedemos definir

objetos particulares. En programación orientada a objetos se dice que un objeto es una instancia

de la clase, es decir un objeto, es un caso particular del conjunto de objetos que comparten

características similares, definidas en la clase.

Un ejemplo servirá para clarificar estos conceptos. Definamos a la clase persona, esta clase

tendrá ciertos atributos, por ejemplo edad, sexo y nombre entre otros. Las acciones o

comportamientos que podemos definir para esta clase son, por ejemplo, respirar, caminar, comer,

etcétera. Estas son, para nuestro caso, las características que definen a nuestra clase persona.

Un objeto instanciado de está clase podría ser Patricia Palacios, de sexo Femenino y de 32 años

de edad, quién se encuentra caminando. Estos atributos son conocidos formalmente en

programación orientada a objetos como variables de instancia por que contienen el estado de un

objeto particular en un momento dado, en este caso Patricia Palacios. Así mismo, los

comportamientos son llamados métodos de instancia porque nos muestran el comportamiento de

un objeto particular de la clase.

Page 26: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

26

Hay que tener cuidado de no confundir un objeto

con una clase, una gelatina de fresa y una

gelatina de limón son objetos de la misma clase (o

del mismo molde), pero con atributos (o sabores)

diferentes.

Clases y Objetos en C++.-

Una clase equivale a la generalización o abstracción de un tipo específico de objetos. Los

polígonos con tres lados iguales podrían ser generalizados, por ejemplo, en una clase que

llamaremos "trianguloEquilatero". Hablar de clase es, así, hablar de una determinada clase de

objetos. En C++ una clase ("class", según la terminología del lenguaje) es un tipo definido por el

usuario. De esta forma, y siguiendo con el ejemplo cinematográfico, el objeto abstracto "Sala de

Cine" sería implementado como la clase "SalaDeCine" en C++, habiendo definido así un nuevo

tipo de dato abstracto, que será manejado por el compilador de parecida forma a como lo hace

con los tipos predefinidos (int, char, float, etc.). Podremos, así, como ya veremos, declarar un

determinado objeto del tipo (o de la clase) "SalaDeCine".

Un objeto es una encapsulación abstracta de información, junto con los métodos o

procedimientos para manipularla. "un objeto contiene operaciones que definen su comportamiento

y variables que definen su estado entre las llamadas a las operaciones". Bueno, pensemos en un

ejemplo asequible: una sala de cine. Imaginemos un objeto del tipo "sala de cine" donde los datos,

variables o información estarían constituidos por los espectadores; imaginemos también (y esto es

importante) que los espectadores no pudieran moverse por sí solos dentro de la sala (como si

estuvieran catatónicos). ¿Quiénes serían los manipuladores, métodos, procedimientos u

operaciones encargados de manipular a los espectadores? Indudablemente los acomodadores de

la sala, aunque también el personal directivo de la misma.

Envió de mensajes.-

Un mensaje representa una acción a tomar por un determinado objeto. En el ejemplo anterior, la

orden "que comience la proyección de la película" podría ser un mensaje dirigido a un objeto del

tipo "sala de cine". Otro mensaje podría ser la orden de "desalojo de la sala" en funciones no-

continuas. Notemos que el mensaje se refiere únicamente a la orden, y no tiene que ver con la

Page 27: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

27

forma como ésta es respondida. En C++ un mensaje equivale al PROTOTIPO de una función

miembro en la descripción de una clase. O sea, si pensamos en una posible función

"especialmente restringida a un objeto" (que en C++ se denomina función miembro) tal como

"empezarProyeccion", el mensaje estaría representado por el prototipo de tal función, y no por su

definición o implementación específica.

El "envío de un mensaje" a un objeto equivale, en C++, como ya he indicado, a una llamada a una

función miembro de la clase correspondiente al objeto. En realidad las expresiones "métodos",

"envío de mensajes", "instancias de variables", etc. pertenecen originariamente al lenguaje

Smalltalk. Volvamos al ejemplo del cine y "lancemos mensajes":

SalaDeCine cineParadox;

SalaDeCine *punteroASalaDeCine;

Hemos declarado por un lado un objeto denominado cineParadox, del tipo definido-por-el-usuario

SalaDeCine. Seguidamente hemos declarado un puntero al mismo tipo de dato abstracto.

El mensaje "que comience la proyección de la película", dirigido a la sala "Cine

Paradox", podría ser codificado así:

cineParadox.empezarProyeccion ();

Esto es, el mensaje o llamada de función se ha dirigido directamente al objeto. Pero veamos el

siguiente código:

punteroASalaDeCine = new SalaDeCine( cinePalafox );

punteroASalaDeCine->empezarProyeccion;

Vemos, pues, que la notación '.' sirve para enviar mensajes directamente a un objeto, mientras

que '->' se usa para el envío de mensajes a los objetos a través de punteros que apunten a los

mismos.

Cabría notar aquí, redundando en lo apuntado anteriormente, que el prototipo de función

empezarProyeccion () corresponde al mensaje, mientras que el método estaría constituido por la

implementación de la definición de la función miembro empezarProyeccion (). El mensaje

Page 28: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

28

corresponde al "qué", mientras que el método corresponde al "cómo". Así, el método podría

definirse -en la clase SalaDeCine- de la forma:

void SalaDeCine::empezarProyeccion()

{

// apaga música y luces y comienza proyección

ponerMusicaDeFondo( OFF );

ponerLucesSala( OFF );

marchaProyector( ON );

}

En este caso el mensaje empezarProyeccion podría ser enviado al objeto cineParadox por un

objeto cronómetro con un horario determinado.

Pudiera parecer, por otra parte, que el método o función miembro empezarProyeccion está

compuesto por funciones del tipo usado en programación estructurada, pero en realidad se trata

de mensajes dirigidos al mismo objeto (en este caso concreto, cineParadox), dado que, por

ejemplo, el código

marchaProyector( ON );

Equivale a:

this->marchaProyector( ON );

En donde this es un puntero implícito al objeto

Encapsulamiento.-

¿Qué es, por otro lado, la encapsulación? Lo que de inmediato nos sugiere el vocablo es la acción

de encapsular, de encerrar algo en una cápsula, como los polvitos medicinales que nos venden en

las farmacias. Podemos decir que la encapsulación se refiere a la capacidad de agrupar y

condensar en un entorno con límites bien-definidos distintos elementos. El fenómeno de la

encapsulación podría darse, por ejemplo, en el conjunto de herramientas y elementos

heterogéneos que integran es un decir la caja de herramientas de un fontanero: la caja es la

cápsula y todo lo que haya dentro es lo que hemos encapsulado. Pero no nos perdamos: la

Page 29: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

29

cualidad de "encapsulación" la aplicaremos únicamente a abstracciones: o sea, afirmar que una

caja de herramientas concreta encapsula un bocadillo, un martillo y un limón constituye una cierta

trasgresión léxica. Cuando hablemos de encapsulación en general siempre nos referiremos, pues,

a encapsulación abstracta. Las dos propiedades expuestas están, como vemos y en lo que a la

POO concierne, fuertemente ligadas. Dicho de manera informal, primero generalizamos (la

abstracción) y luego decimos: la generalización está bien, pero dentro de un cierto orden: hay que

poner límites (la encapsulación), y dentro de esos límites vamos a meter, a saco, todo lo

relacionado con lo abstraído: no sólo datos, sino también métodos, comportamientos, etc. Pero,

bueno ¿y esto es POO? Pues sí, esta es una característica fundamental de POO.

En lo que a C++ se refiere, la unidad de abstracción y encapsulación está representada por la

Clase, (class). Por un lado es una abstracción pues, de acuerdo con la definición establecida

anteriormente, es en ésta donde se definen las propiedades y atributos genéricos de

determinados objetos con características comunes (recordemos el ejemplo de la sala de cine). La

Clase es, por otro lado, una encapsulación porque constituye una cápsula o saco que encierra y

amalgama de forma clara tanto los datos de que constan los objetos como los procedimientos que

permiten manipularlos. Las Clases se constituyen, así, en abstracciones encapsuladas.

Abordemos ahora el tema en su parte cruda y echémosle un vistazo a parte del código de lo que

podría ser una descripción de una clase que denominaremos Racional:

Class Racional {

friend Racional& operator+( Racional, Racional );

// ...

private:

int numerador;

int denominador;

// ...

void simplificaFraccion();

// ...

public:

// ...

void estableceNumerador( int );

void estableceDenominador( int );

// ...

Page 30: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

30

};

Observemos que en la clase Racional, abstracción del conjunto de los números racionales, de su

representación formal (x/y: equis dividido por y) y operaciones (a/b + c/d), aparecen encapsulados

tanto los datos internos (numerador, denominador) como las funciones miembro para el

tratamiento de éstos (el operador suma de quebrados, el procedimiento para cambiar el

denominador, el método para simplificar fracciones, etc.).

Bueno, la verdad es que en el código anterior observamos muchas más cosas: el símbolo de

comentario //, que anula lo escrito (a efectos de compilación) desde su aparición hasta el fin de la

línea; las etiquetas private y public, que determinan cómo y quién accederá a los datos y

funciones de la clase; la palabra clave friend, que indica la calificación como "amiga de la clase

Racional" de una función que suma Racionales (¿Racionales? ¿alguna forma de typedef?) y a la

que se dará el tratamiento de "operador suma" (operator +), devolviendo una "referencia" (&) a

Racional (¿referencia a Racional? ¿alguna forma de puntero?). Vale, vale: son muchas cosas en

las que todavía no podemos entrar y que en su momento veremos en detalle. Bástenos saber, por

ahora, que tal código intenta describir el comportamiento y la esencia de un tipo de datos bien

conocido por todos (el conjunto de los números racionales, de la forma x/y, donde x e y son

números enteros) pero no incluido como tipo predefinido2 por la mayoría de compiladores. Esta

carencia es la que intentamos suplir con el código expuesto: una vez descrita la clase (class), se

habrá traspasado al compilador el conocimiento de, para él, un nuevo tipo de número: el

"Racional". Retengamos de momento únicamente esta última idea pensando, como único alivio,

que cuanto más veamos una "rareza", antes dejara ésta de serlo.

Salvado el inciso, quizá puedan apreciarse mejor las características de abstracción y

encapsulación notadas en la siguiente porción de la definición de la clase SalaDeCine, tomada del

ejemplo "cinematográfico":

Class SalaDeCine {

private:

int aforoSala;

char *nombreSala;

// ...

public:

void alarmaEnCasoDeIncendio();

Page 31: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

31

void empezarSesion();

// ...

};

La clase SalaDeCine aglutina, según lo expuesto, tanto los datos correspondientes a las salas de

cine en general (capacidad de la sala, nombre de la misma, etc.) como los métodos de respuesta

a los mensajes dirigidos a los objetos "salas de cine" (alarma por incendio, etc.). O sea, la clase

SalaDeCine es la abstracción resultante de la asimilación intelectual de todas las posibles salas

de cine, vistas o soñadas: aquí tenemos la cualidad de abstracción. La misma clase encapsula,

por otro lado, datos y funciones dentro de un límite bien definido: ¿cuál? ¿Las paredes del

edificio? ¡No, vaya! El límite es el mismo que el que separa una sala de cine de un almacén de

frutas: su propia definición, que en este caso coincide con el bloque de la clase. La cápsula esta

formada por los brazos {} que encierran el cuerpo de la clase.

Conviene notar que los tipos de datos incorporados al compilador (int, char, long, etc.) son

realmente abstracciones encapsuladas de datos y métodos. Consideremos el siguiente código:

float resta, minuendo, sustraendo;

resta = minuendo - sustraendo;

El operador (-) representa aquí la operación de substracción entre dos variables del tipo

predefinido 'float'. Pero tal operador es, realmente, un mensaje que el objeto sustraendo (un

número float) le envía al objeto minuendo (otro número float) , y el método de respuesta a tal

mensaje está implementado en la encapsulación de tipo float predefinida en el compilador, que

resta del minuendo el sustraendo y devuelve un valor de tipo float. Tal método es formalmente

diferente del que podría ser implementado para la resta de dos variables de tipo int (no hay parte

decimal, etc.), o aún de un tipo definido por el usuario (user-defined). Aclaremos esto: el

compilador sabe que una operación sobre floats no tiene por

Constructores Y Destructores.-

Un constructor es un procedimiento especial de una clase que es llamado automáticamente

siempre que se crea un objeto de esa clase. Su función es iniciar el objeto.

Page 32: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

32

Un destructor es un procedimiento especial de una clase que es llamado Automáticamente

siempre que se destruye un objeto de esa clase. Su función es realizar cualquier tarea final en el

momento de destruir el objeto.

Historia del java.-

A pesar de lo que pueda se pueda en un principio pensar, Java no surgió inicialmente como un

lenguaje de programación orientado a la web. Los orígenes se remontan al año 1991 cuando

Mosaic (uno de los primeros browsers) o la World Wide Web no eran más que meras ideas

interesantes. Los ingenieros de Sun Microsystems estaban desarrollando un lenguaje capaz de

ejecutarse sobre productos electrónicos de consumo tales como electrodomésticos.

Simultáneamente James Gosling, el que podría considerarse el padre de Java, estaba trabajando

en el desarrollo de una plataforma software barata e independiente del hardware mediante C++.

Por una serie de razones técnicas se decidió crear un nuevo lenguaje, al que se llamó Oak, que

debía superar algunas de las deficiencias de C++ tales como problemas relacionados con la

herencia múltiple, la conversión automática de tipos, el uso de punteros y la gestión de memoria

El lenguaje Oak se utilizó en ciertos prototipos de electrónica de consumo pero en un principio no

tuvo el éxito esperado dado que la tecnología quizás era demasiada adelantada a su tiempo. No

obstante lo positivo de estos primeros intentos fue que se desarrollaron algunos de los elementos

precursores de los actuales componentes Java; componentes tales como el sistema de tiempo de

ejecución y la API (Application Program Interface “Interfaz para la Programación de

Aplicaciones”).

En 1994 eclosionó el fenómeno web y Oak fue rebautizado como Java. En un momento de

inspiración, sus creadores decidieron utilizar el lenguaje para desarrollar un browser al que se

llamó WebRunner, que fue ensayado con éxito, arrancando en ese momento el proyecto

Java/HotJava. HotJava fue un browser totalmente programado en Java y capaz así mismo de

ejecutar código Java.

Page 33: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

33

A lo largo de 1995 tanto Java, su documentación y su código fuente como HotJava pudieron

obtenerse para múltiples plataformas al tiempo que se introducía soporte para Java en la versión

2.0 del navegador Netscape.

La versión beta 1 de Java despertó un inusitado interés y se empezó a trabajar para que Java

fuera portable a todos los sistemas operativos existentes.

En diciembre de 1995 cuando se dio a conocer la versión beta 2 de Java y Microsoft e IBM dieron

a conocer su intención de solicitar licencia para aplicar la tecnología Java, su éxito fue ya

inevitable.

El 23 de enero 1996 se publicó oficialmente la versión Java 1.0 que ya se podía obtener

descargándola de la web.

A principios de 1997 aparece la versión 1.1 mejorando mucho la primera versión.

Java 1.2 (Java 2) apareció a finales de 1998 incorporando nuevos elementos. Según Sun esta era

la primera versión realmente profesional.

En mayo del 2000 se lanza la versión 1.3 del J2SE (Java 2 Standar Edition) y hace unos meses,

en febrero de este año, se lanzó la versión 1.4 (la versión 1.4.1 es ya una beta).

¿Qué es Java?

Page 34: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

34

Java no es sólo un lenguaje de programación, Java es además un sistema de tiempo de

ejecución, un juego de herramientas de desarrollo y una interfaz de programación de aplicaciones

(API). Todos estos elementos así como las relaciones establecidas entre ellos se esquematizan

en la siguiente figura.

Fig. 2 Las interioridades de Java

El desarrollador de software escribe programas en el lenguaje Java que emplean paquetes de

software predefinidos en la API. Luego compila sus pr ogramas mediante el compilador Java y el

resultado de todo ello es lo que se denomina bytecode compilado. Este bytecode es un fichero

independiente de la plataforma que puede ser ejecutado por máquina virtual Java. La máquina

Page 35: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

35

virtual puede considerarse como un microprocesador que se apoya encima de la arquitectura

concreta en la que se ejecuta, interactuando con el sistema operativo y el hardware, la máquina

virtual es por tanto dependiente de la plataforma del host pero no así el bytecode. Necesitaremos

ta ntas máquinas virtuales como plataformas posibles pero el mismo bytecode podrá ejecutarse

sin modificación alguna sobre todas ellas.

Así pues, las claves de la portabilidad de Java son su sistema de tiempo de ejecución y su API.

Los potentes recursos para el trabajo de ventanas y redes incluidos en la API de Java facilitan a

los programadores el desarrollo de un software que resulte a la vez atractivo e independiente de la

plataforma.

Ejemplo.-

1) Ingrese dos Números enteros y determine cual es el mayor de ellos.(Completar)

import java.io.*;

public class Ejemplo1

{

public static void main(String args[]) throws Exception

{

int a,b ,mayor;

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

{

System.out.println("Ingrese el primer numero...: " );

a= Integer.parseInt(br.readLine());

System.out.println("Ingrese el segundo numero...: " );

b= Integer.parseInt(br.readLine());

if(a > b)

{

mayor = a;

}else{

mayor = b;

}

}

}

}

Page 36: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

36

EJERCICIOS.-

1) Ingrese 3 números enteros positivos y determine cuál de ellos es el mayor y menor.

2) Genere la siguiente serie:

X = 1/1 - 1/22 + 1/ 32 - 1/42 + - …..

3) Dado un numero entero psitivo escribir en el orden inverso.

4302 ----------------> 2034

Page 37: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

37

TEMA III

SOBRECARGA

Introducción.-

La sobrecarga es una propiedad que describe una característica adecuada que utiliza el mismo

nombre de operación para representar operaciones similares que se comporta de modo diferente

cuando se aplican a las clases diferentes. Por consiguiente, los nombres de las operaciones se

pueden sobrecargar, esto es, las operaciones se definen en clase diferente y pueden tener

nombres idénticos, aunque su código programa puede diferir.

Si los nombres de una operación se utilizan para nuevas definiciones en clases de una jerarquía la

operación a nivel inferior se dice que anula la operación a un nivel más alto.

Actualmente la sobrecarga solo se aplica a operaciones. Aunque es posible extender la propiedad

a atributos y relaciones especificas del modelo propuesto.

Los lenguajes de programación convencionales soportan sobrecarga para algunas de las

operaciones sobre algunos tipos de datos, como enteros, reales y caracteres. Los sistemas

orientados a objetos dan un poco más en la sobrecarga y la hacen disponible para operaciones

sobre cualquier tipo de objeto.

Sobrecarga de funciones en C++ y java.-

Sobrecarga en c++:

C++ permiten que sean definidas varias funciones del mismo nombre, siempre que estos nombres

de funciones indiquen diferentes conjuntos de parámetros (por lo menos en lo que se refiere a sus

tipos). Esta capacidad se llama homonimia de funciones. Cuando se llama a una función

homónima, el compilador C++ selecciona de forma automática la función correcta examinando el

número, tipos y orden de los argumentos de la llama. La homonimia de la función de utiliza para

crear varias funciones del mismo nombre, que ejecutan tares similares, sobre tipos de datos

diferentes.

Page 38: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

38

#include<iostream.h>

// existen dos metodos con el nombre (en ese caso se realiza la sobrecarga) y nos devuelven //dos resultados con dos tipos diferente

(entero y Double).

int cuadrado(int x){return x * x;}

double cuadrado (double y) {return y * y;}

main()

{

cout << "El cuadrado de 7 es "

<< cuadrado(7)

<< "\n el cuadrado de double 7.5 es "

<< cuadrado(7.5)<< "\n";

cin.get();

return 0;

}

Sobrecarga en Java:

Para empezar, una breve reseña acerca de la sobrecarga de métodos. Decimos que un método

ha sido “sobrecargado” cuando han sido creados múltiples métodos con el mismo nombre, pero

especificando distintos argumentos (en número o tipo).

Como ejemplo veamos este trozo de código:

class Dog {

public static void main(String[] args) {

bark(); // Imprime sin parametros

bark(100); // Imprime con parametros

}

public static void bark() {

System.out.println("no parametros");

}

public static void bark(int decibels) {

System.out.println("existen parametros");

}

}

Page 39: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

39

En este caso, se ejecuta un método u otro dependiendo de si se especifica un parámetro en la

llamado o no.

Supongamos ahora una situación como la siguiente:

class Animal {}

class Dog extends Animal {}

class TestOverload {

public static void main (String[] args) {

Animal a1 = new Animal();

Animal a2 = new Dog();

Dog d = new Dog();

makeSound(a1);

makeSound(a2);

makeSound(d);

}

public static void makeSound(Animal a) {

System.out.println("Realiza sonidos de animal");

}

public static void makeSound(Dog d) {

System.out.println("Realiza sinidos de perro");

}

}

La razón es que Java determina qué versión del método sobrecargado va a ser llamada en

tiempo de compilación.

Así pues, aunque a referencia a2 apunta a una instancia del objeto Dog, el compilador utiliza una

referencia de tipo Animal para determinar cual de los métodos será llamado.

A continuación un ejemplo de lo peligroso que puede llegar a resultar esto. Un ejemplo en que la

salida del programa no permite distinguir cual de los métodos está siendo ejecutado:

class Animal {

public void doMakeNoise() {

System.out.println("Hacer como animal");

Page 40: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

40

}

}

class Dog extends Animal {}

class TestOverload {

public static void main (String[] args) {

Animal a1 = new Animal();

Animal a2 = new Dog();

Dog d = new Dog();

makeSound(a1);

makeSound(a2);

makeSound(d);

}

public static void makeSound(Animal a) {

a.doMakeNoise();

}

public static void makeSound(Dog d) {

d.doMakeNoise();

}

}

Esto se debe al polimorfismo y la llamada a doMakeNoise añadida en cada uno de los métodos

makeSound.

Evidentemente, esto no es un problema si la funcionalidad final es la misma (pero en este caso se

necesitaría una reorganización de código).

Lo importante, en cualquier caso, es tener en mente que Java determina el método sobrecargado

a ejecutar en tiempo de compilación.

Sobrecarga de operadores.-

Sobrecarga de operadores en C++:

La sobrecarga de operadores es utilizada en la Programación Orientada a Objetos, esta hace

posible manipular objetos de clases con operadores estándar +, *, [ ], y <<. Los tipos de

operadores a tratar serán los binarios y unitarios.

Para sobrecargar un operador, y aplicarlo en operaciones entre clases, se usa la función especial

“operator” seguido del operador que se sobrecargara, la sintaxis es la siguiente:

Page 41: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

41

tipo nombre_clase::operator # (lista de parámetros);

Donde:

tipo: es el tipo de retorno de la operación de sobrecarga, por lo general es la referencia del

objeto.

nombre_clase: es el nombre de clase en la que estamos aplicando sobrecarga.

operator: función especial para sobrecarga.

operador que estamos sobrecargando, debe ser uno de los que esta en la tabla mas

abajo.

Lista de parámetros: es la lista de objetos (en referencia) o variables que se procesaran en

la sobrecarga, si la operación es unaria no tendrá parámetros, si la

Operación es binaria tendrá un parámetro.

Como ejemplo se muestra la sobrecarga del operador suma:

numero& numero::operator + (numero &p){

numero *salida = new numero; //creando objeto de retorno

salida->n = this->n + p.n; //operacion suma

return *salida; //retorno

}

Operadores que se pueden sobrecargar

Operadores Unitarios son:

New delete new [ ] delete

++ Incremento.

-- Decremento.

() Llamada a función

[ ] Indexación

Page 42: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

42

Ejemplo.-

En este ejemplo se muestra la sobrecarga de operadores binarios (suma: +), operadores unarios

(incremento: ++), y la interacción con variables normales (igualación: =). Sobre la

clase numero:

#include <iostream>

//using namespace std;

class numero {

private:

int n;

public:

numero() : n(0) {} //constructor sin parametros

numero(int a) { this->n = a; } //constructor con parametros

int ver() { return n; } //muestra valor en n

numero& operator + (numero &p); //declaracion sobrecarga operador binario suma

numero& operator =(int a); //declaracion sobrecarga operador igualacion

numero& operator ++(); //declaracion sobrecarga operador unario incremento

};

//definicion de la sobrecarga del operador de suma

numero& numero::operator + (numero &p){

numero *salida = new numero;

salida->n = this->n + p.n;

return *salida;

}

//cuerpo de la sobrecarga del operador de valoración

numero& numero::operator =(int a){

this->n=a;

return *this;

}

//definicion de la sobrecarga del operador de incremento

numero& numero::operator ++ (){

this->n ++;

return *this;

}

// prueba para la clase numero

int main(){

Page 43: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

43

numero x(4), y(2), z; //instancias x, y, z de objeto numero

z = x + y; //implementando operador binario suma

cout << "\n suma:\n \t x(" << x.ver()<<")+y("<<y.ver()<<") = z("<<z.ver()<<")"<<endl;

z=2; //implementando operador de igualacion con un entero

cout << "\n igualacion \"z=\" \n \t z="<<z.ver()<<endl;

++z; //implementando operador unario de incremento

cout << "\n incremento \"++\" \n \t ++z="<<z.ver()<<endl;

cin.get();

return 0;

}

Sobrecarga de operadores en java:

En java sólo existe la sobrecarga de funciones (métodos); Los operadores que existen ya vienen

sobrecargados por el compilador, (por ejemplo la “+” para sumar números o concatenar cadenas).

public class sobrecarga

{

public static void main(String args[])

{

int a , b;

String c , d;

a = 2;

b = 3;

c = "Hola ";

d ="Mundo";

/*

* El operador + es un perador sobrecargado para sumar numeros y concatenar cadenas

*/

System.out.println(a + b);

System.out.println( c + d );

}

}

Ejemplos.-

class Numero{

friend ostream& operator<<(ostream&salida , const Numero&a);

friend istream& operator>>(istream&salida, Numero &a);

friend Numero operator+(const Numero &a,const Numero &b);

friend Numero operator-(const Numero &a,const Numero &b);

friend Numero operator/(const Numero &a,const Numero &b);

friend Numero operator*(const Numero &a,const Numero &b);

public: Numero(double x = 0);

Page 44: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

44

void establecer(double x); private: double x;};

//Constructor

Numero::Numero(double b)

{

establecer( b );

}

void Numero::establecer(double a)

{

x = a;

}

//Sobrecarga de operadores, mediante funciones friend

Numero operator+(const Numero &a ,const Numero &b)

{

Numero temporal;

temporal.x = a.x + b.x;

return temporal;

}

Numero operator-(const Numero &a ,const Numero &b)

{

Numero temporal;

temporal.x = a.x - b.x;

return temporal;

}

Numero operator/(const Numero &a ,const Numero &b)

{

if (b.x == 0){

cout << "error de division por 0. Detiene ejecucion." << endl;

// exit(1);

}

Numero temporal;

temporal.x = a.x / b.x;

return temporal;

}

Numero operator*(const Numero &a ,const Numero &b)

{

Numero temporal;

temporal.x = a.x * b.x;

return temporal;

}

ostream &operator<<(ostream& salida , const Numero& a)

{

Page 45: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

45

salida << a.x ; return salida;

}

istream &operator>>(istream& entrada, Numero & a)

{

entrada >> a.x;

return entrada;

}

//Probamos la clase.int

main()

{

cout << "Tres numeros porfa: " << endl;

Numero A; cin >> A;

Numero B; cin >> B;

Numero C; cin >> C;

cout << "A = " << A << ", B = " << B << ", C = " << C << endl;

Numero resultado; resultado = A - B + C;

cout << "Resultado = A - B + C = " << resultado << endl;

resultado = A / B ;

cout << "Resultado = A / B = " << resultado << endl;

resultado = A * resultado;

cout << "Resultado = A * resultado = " << resultado << endl;

resultado = B * resultado / A;

cout << "Resultado = B * resultado / A = " << resultado << endl;

cin.get();

return 0;

}

Page 46: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

46

TEMA IV

HERENCIA

Introducción.-

La herencia permite el acceso automático a la información contenida en otra clase. De esta forma

la reutilización del código está garantizada. Con la herencia todas las clases están clasificadas en

una jerarquía estricta. Cada clase tiene su superclase (la clase superior en la jerarquía).

Herencia.-

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

la clase base. Las clases derivadas pueden heredar el código y os datos de su clase base,

añadiendo su propio código especial y datos a ellas , incluso cambiar a aquellos elementos de la

clase base que necesitan ser diferentes.

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

Herencia simple.-

Es cuando una clase derivada hereda de una única clase, es decir una clase derivada sólo tiene

un padre o ascendiente. Por su parte una clase base puede tener tantos descendientes como

sean necesarios sin limitación alguna. Es un sistema jerárquico en forma arborescente, similar a la

estructura de directorios de algunos sistemas operativos. La forma general de la herencia en C++

es:

class <nombre_clase_derivada>: [<acceso>] <nombre_clase_heredada> {

Page 47: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

47

// Cuerpo de la declaración de la clase

};

El nombre_clase_heredada se refiere a una clase base declarada previamente. Ésta pude estar

ya compilada, o puede que se declare en el mismo programa que la derivada, en este segundo

caso se debe declarar la clase base antes de la derivada, o al menos declarar el nombre de la

clase base como una referencia anticipada.

El acceso puede ser private, protected o public. Si se omite se supone que el acceso es

private5, de forma que si se quiere dar un acceso public o protected se debe hacer

explícitamente.

Los elementos private de la clase base son inaccesibles para la clase derivada, sea cual se el

acceso. Si el acceso es public, todos los elementos public y protected de la clase base

seguirán siendo public y protected respectivamente en la clase derivada. Si el acceso es

private, entonces todos los elementos public y protected de la clase base pasarán a ser

elementos private de la clase derivada. Si el acceso es protected todos los miembros public y

protected de la clase base se convierten en elementos protected de la clase derivada. En la

siguiente tabla se resumen los especificadores de acceso:

Se puede añadir a la clase derivada datos y funciones miembro. Dentro de las funciones miembro

de la clase derivada se puede llamar a las funciones miembro y manejar los datos miembro que

estén en la sección pública y protegida de la clase base. En una clase derivada se heredan todos

los datos miembro de la clase base excepto los estáticos. Algunas funciones miembro no se

Page 48: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

48

heredan de forma automática. Éstas son los constructores, el destructor, las funciones amigas, las

funciones estáticas de la clase, y el operador de asignación sobrecargado.

Se va a ilustrar lo que se ha dicho hasta el momento de la herencia en los siguientes ejemplos:

// Programa: Herencia Simple

// Fichero: HER_SIM1.CPP

#include <iostream.h>

// Se define una clase sencilla

class Primera {

protected:

int i, j;

public:

Primera() {i=j=0;}

Primera(int a, int b) {i=a; j=b;}

void Entra_ij(int a, int b) {i=a; j=b;}

void Muestra_ij (void) {cout << "\n" << i << " " << j;}

};

// Se tiene otra clase, Segunda, que es derivada de la clase Primera

// Las variables i, j de Primera pasan a ser miembros protected de

// Segunda, ya que el acceso es public

class Segunda: public Primera {

int k;

public:

void Entra_k(int a) {k=a;}

void Muestra_k (void) {cout << "\n" << k;}

};

// Se tiene otra clase, Tercera, que es derivada de la clase Segunda

// Las variables i, j de Primera pasan a ser miembros protected de

// Tercera, ya que el acceso es public. Sin embargo no tiene acceso a k de

// Segunda, ya que esta variable es private.

class Tercera: public Segunda {

public:

void f (void) { i=j=2;}

};

void main (void)

{

Primera P(1,2);

Segunda S;

Tercera T;

S.Entra_ij(3,4);

Page 49: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

49

S.Entra_k(5);

P.Muestra_ij(); // Saca 1 2

S.Muestra_ij(); // Saca 3 4

S.Muestra_k(); // Saca 5

T.f();

T.Muestra_ij(); // Saca 2 2

T.Entra_k(3);

T.Muestra_k(); // Saca 3

S.Muestra_k(); // Saca 5

T.Entra_ij(5,6);

T.Muestra_ij(); // Saca 5 6

P.Muestra_ij(); // Saca 1 2

}

Ventajas e inconvenientes de la derivación Privada y Protegida.-

Cuando se utiliza el especificador de acceso private o el especificador de acceso protected en la

herencia, se está asegurando que sólo las partes públicas de la clase derivada podrán ser

utilizadas por el resto del programa, y por las otras clases derivadas que se derivan a partir de

ésta.

El acceso private “corta” el acceso, no la herencia, a partir de la clase derivada. El acceso

protected “corta” el acceso, no la herencia, desde el exterior, pero no desde las clases derivadas.

Los inconvenientes llegan porque al utilizar estos tipos de accesos se está complicando el árbol

de herencia, alcanzando una mayor complejidad la determinación por parte del programador, o de

alguien que lea el programa, del derecho de acceso a los miembros de cada clase. Estos tipos de

derivaciones se emplean muy poco, hay que ser muy cuidadoso cuando se utilizan, y tener unos

buenos motivos. Se puede considerar similar a tener un miembro que es una instancia de otra

clase diferente.

Control de acceso a la clase base.-

Dado que una clase derivada se puede entender como un superconjunto de la clase base, que va

a contener todos los miembros de la clase base, una instancia de la clase derivada se puede

emplear de forma automática como si fuera una instancia de la clase base. El caso contrario no es

cierto, no se puede tratar un objeto de la clase base como si fuera un objeto de una clase

derivada, ya que el objeto de la clase base no tiene todos los miembros de la clase derivada.

Page 50: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

50

El siguiente programa muestra como funciona el tema de las conversiones de un objeto de una

clase derivada a uno de la clase base.

// Programa: Herencia Simple: Conversiones entre clases

// Fichero: HER_SIM3.CPP

#include <iostream.h>

class Primera {

protected:

int i, j;

public:

Primera() {i=j=0;}

Primera(int a, int b) {i=a; j=b;}

void Entra_ij(int a, int b) {i=a; j=b;}

void Muestra_ij (void) {cout << "\n" << i << " " << j;}

};

class Segunda: public Primera {

int k;

public:

void Entra_k(int a) {k=a;}

void Muestra_k (void) {cout << " " << k;}

};

void main (void)

{

Primera P(1,2);

Segunda S;

S.Entra_ij(3,4);

S.Entra_k(5);

P.Muestra_ij(); // Saca 1 2

S.Muestra_ij(); // Saca 3 4

S.Muestra_k(); // Saca 5

P=S; // Correcto

P.Muestra_ij(); // Saca 3 4

}

Un punto mucho más interesante es la conversión de un puntero a una clase derivada, en un

puntero a una clase objeto.

Para ver este caso, se va a presentar el siguiente ejemplo. Se tiene la siguiente escala de

empleados, que se va a representar como una estructura de clases derivadas.

Page 51: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

51

// Programa: Empleados

// Fichero: EMPLE1.CPP

#include <iostream.h>

#include <string.h>

class Empleado {

protected:

char nombre[30];

public:

Empleado();

Empleado(char *nom);

char *Nombre() const;

};

class aHoras: public Empleado {

protected:

float horas;

float salario;

public:

aHoras ( char *nom );

void FijaSalario ( float s );

void FijaHoras ( float h );

void SacaNombre (void) const;

};

class Gerente: public Empleado {

float salario_semanal;

public:

Gerente ( char *nom );

void FijaSalario (float s);

};

class Vendedor: public aHoras {

float comision;

Page 52: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

52

float ventas;

public:

Vendedor (char *nom );

void FijaComision ( float c );

void FijaVentas ( float v );

void SacaComision (void);

};

// Funciones miembro de la clase Empleado

Empleado::Empleado () {

memset ( nombre, ' ', 30);

}

Empleado::Empleado( char * nom) {

strncpy (nombre, nom, 30);

}

char *Empleado::Nombre() const {

return (char *)nombre;

}

// Funciones miembro de la clase aHoras

aHoras::aHoras ( char *nom ):Empleado (nom) {

horas=0.0; salario=0.0;

}

void aHoras::FijaSalario (float s) {

salario=s;

}

void aHoras::FijaHoras (float h) {

horas=h;

}

void aHoras::SacaNombre (void) const {

cout << "\nEl nombre del trabajador es: " << nombre;

}

// Funciones miembro de la clase Gerente

Gerente::Gerente ( char *nom ):Empleado (nom) {

salario_semanal=0.0;

}

void Gerente::FijaSalario (float s) {

salario_semanal=s;

}

// Funciones miembro de la clase Vendedor

Vendedor::Vendedor (char *nom ):aHoras(nom) {

comision=0.0;

ventas=0.0;

Page 53: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

53

}

void Vendedor::FijaComision (float c) {

comision=c;

}

void Vendedor::FijaVentas (float v) {

ventas=v;

}

void Vendedor::SacaComision (void) {

cout << "\n" << comision;

}

void main(void)

{

Empleado *ptr_emp;

aHoras a1("Juan Antonio P.");

Gerente g1("Alberto Lamazares");

Vendedor v1("Ana Elena");

ptr_emp=&a1;

cout << "\n" << ptr_emp->Nombre();

ptr_emp=&g1;

cout << "\n" << ptr_emp->Nombre();

ptr_emp=&v1;

cout << "\n" << ptr_emp->Nombre();

}

Se puede utilizar un puntero a Empleado para apuntar directamente a una instancia de aHoras, a

una instancia de Gerente, o a una instancia de Vendedor. Sin embargo, si la definición de la

clase Vendedor hubiera sido:

class Vendedor : aHoras { ...

Para poder con un puntero a Empleado apuntar a una instancia de la clase Vendedor se hubiera

tenido que hacer el siguiente casting.

ptr_emp=(Empleado *)&v1;

Esto es debido a que cuando en la herencia se utiliza el método de acceso private o protected no

se puede convertir de forma implícita un puntero de la clase derivada a un puntero de la clase

base.

Page 54: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

54

Cuando se hace referencia a un objeto a través de un puntero, el tipo de puntero determina que

funciones miembro se pueden llamar. Si se utiliza un puntero a una clase base para apuntar a un

objeto de una clase derivada, sólo se podrán utilizar las funciones definidas en la clase base. Por

ejemplo, estudiar el siguiente programa principal.

void main(void)

{

Vendedor v1("Ana Elena"), *v2_ptr;

aHoras *a1_ptr;

v2_ptr=&v1;

a1_ptr=&v1;

a1_ptr->FijaHoras(40.0); // Se llama a aHoras::FijaHoras

v2_ptr->FijaSalario(6.0); // Se llama a aHoras::FijaSalario

// a1_ptr->FijaVentas(1000.0); // Error:no existe aHoras::FijaVentas

v2_ptr->FijaVentas(1000.0); // Se llama a Vendedor::FijaVentas

}

Ambos punteros v2_ptr y a1_ptr apuntan al mismo objeto Vendedor. No se puede llamar a la

función FijaVentas a través de a1_ptr porque la clase aHoras no tiene definida una función que

se llame así.

El caso contrario, que un puntero de una clase derivada apunte a un objeto de la clase base, se

puede hacer mediante un casting apropiado, pero es muy peligroso y no es recomendable

hacerlo, ya que no se está seguro a lo que se apunta. Así el siguiente caso:

void main(void)

{

aHoras a1("Pepe Pérez");

Empleado *ptr = &a1;

Vendedor *v1;

v1=(Vendedor *) ptr; // Lícito pero incorrecto

cout << "\n" << v1->Nombre(); // Saca Pepe Pérez

v1->FijaComision(1.5); // La clase aHoras no tiene

// el miembro FijaComision

v1->SacaComision(); // Saca basura

}

Page 55: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

55

Constructores y destructores en herencia.-

Los constructores y destructores no son heredados por las clases derivadas. Sin embargo, una

instancia de una clase derivada contendrá todos los miembros de la clase base, y éstos deben ser

iniciados. En consecuencia, el constructor de la clase base debe ser llamado por el constructor de

la clase derivada.

Cuando se escribe un constructor para la clase derivada, se debe especificar una forma de inicio

para la base. La forma de hacerlo es utilizar una sintaxis similar a la empleada con los miembros

de inicio para los objetos miembro de una clase. Esto es, se coloca : después de la lista de

argumentos del constructor de la clase derivada, y seguidamente el nombre de la clase base y la

lista de argumentos.

// Programa: Constructores en la herencia

// Fichero: CONSTR.CPP

#include <iostream.h>

class B {

int a, b, c;

public:

B(){a=b=c=0;}

B( int a1, int a2, int a3 )

{

a=a1; b=a2; c=a3;}

void DarValores( int a1, int a2, int a3 )

{

a=a1; b=a2; c=a3;

}

void MostrarEnteros (void)

{

cout << "\n" << a << " " << b << " " << c;

}

};

class C: public B {

float x, y;

public:

C():B(0,0,0) {x=y=0.0;}

C(float a1, float a2, int a3, int a4, int a5) : B(a3, a4, a5)

{

x=a1; y=a2;

Page 56: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

56

}

void MostrarFloat(void)

{

cout << "\n" << x << " " << y;

}

};

void main()

{

C b, c(1, 2, 3, 4, 5);

b.MostrarEnteros();

b.MostrarFloat();

c.MostrarEnteros();

c.MostrarFloat();

}

Cuando se declara un objeto de una clase derivada, el compilador ejecuta el constructor de la

clase base en primer lugar, y después ejecuta el constructor de la clase derivada. Si la clase

derivada tuviera objetos miembro, sus constructores se ejecutarían después del constructor de la

clase base, pero antes que el constructor de la clase derivada. Por lo que respecta a los

destructores en la jerarquía de clases, se llaman en orden inverso de la derivación, es decir de la

clase derivada a la clases base. La última clase creada es la que se destruye en primer lugar.

Herencia Múltiple.-

Una clase puede tener más de una clase base. Esto significa que una clase puede heredar de dos

o más clases. A este fenómeno se le conoce como Herencia Múltiple.

La sintaxis de la herencia múltiple es una extensión de la utilizada para la herencia simple. La

manera de expresar este tipo de herencia es mediante una lista de herencia, que consta de las

clases de las que se hereda separadas por comas. La forma general es:

class < nombre_clase_derivada > : < lista_de_herencia > {

// Cuerpo de la clase

};

Ejemplo: Vamos a ilustrar la herencia múltiple con un sencillo ejemplo.

Page 57: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

57

// Programa: Herencia Múltiple

// Fichero: HER_MUL1.CPP

#include <iostream.h>

class Base1 {

protected:

int b1;

public:

void Inicia_B1(int v) { b1=v; }};

class Base2 {

protected:

int b2;

public:

void Inicia_B2(int v) { b2=v; }

int Ret (void) { return b2; }

};

class Der: public Base1, public Base2 {

public:

void print (void) { cout << "\nb1 = " << b1 << "\tb2 = " << Ret(); }

};

void main (void)

{

Der d;

d.Inicia_B2(78);

d.Inicia_B1(34);

d.print(); // Saca 34 y 78

}

Este es un ejemplo muy sencillo en el que una clase, Der, hereda de otras dos clases Base1,

Base2. En este programa no hay complicaciones de ningún tipo, y no es muy interesante.

En la herencia múltiple las reglas de inicio de los miembros son una extensión de los vistos en la

herencia simple. De igual forma, los mecanismos de herencia de los miembros están gobernados

por las declaraciones de private, protected y public en las clases.

El orden en el que se especifican las clases bases en la lista de herencia sólo afecta al orden en

que se van a invocar los constructores, y destructores de las clases base desde los constructores

y destructores de la clase derivada.

Vamos a ver a continuación otro ejemplo de herencia múltiple, en el que las clases tengan

constructores.

Page 58: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

58

// Programa: Herencia Múltiple

// Fichero: HER_MUL2.CPP

#include <iostream.h>

class A {

protected:

int a;

public:

A (int valor) {

cout << "\nConstructor de A.";

a=valor;

}

~A () { cout << "\nDestructor de A."; }

};

class B {

protected:

int b;

public:

B (int valor) {

cout << "\nConstructor de B.";

b=valor;

}

~B () { cout << "\nDestructor de B."; }

};

// C hereda de A y de B

class C: public A, public B {

public:

C(int v1, int v2) : B(v2), A(v1) {

cout << "\nConstructor de C.";

}

~C () { cout << "\nDestructor de C."; }

int operar (void) { return a*a+b*b; }

};

void main (void)

{

C obj(4, 5);

cout << "\n" << obj.operar();

};

Page 59: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

59

De lo que se deduce que las clases bases se construyen en el orden en que aparecen en la

declaración de C. Una vez que las clases han recibido los valores iniciales, se ejecuta el

constructor de la clase derivada.

Mientras que ninguna de las clases base tenga constructor, o que todas las clases bases tengan

un constructor por defecto, la clase derivada no tiene porque tener constructor. De todas formas

es una buena práctica de programación que todas las clases tengan un constructor para de esta

forma contar con un mecanismo que permita pasar argumentos a los constructores de sus clases

bases.

En cuanto a los destructores, decir que al igual que en la herencia simple, se llaman en orden

inverso a la derivación.

Los mecanismos de herencia múltiple permiten al programador situar la información donde sea

necesario. En contra, la herencia simple puede situar parte de los datos donde no se requieran.

Supóngase que se tiene el siguiente árbol de jerarquía, y sólo se desea que existan datos de otra

clase X en las clases E y F.

Con herencia simple se tendría el siguiente esquema:

Page 60: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

60

Con herencia múltiple la solución sería:

La herencia múltiple es más apropiada para definir objetos compuestos o una combinación de

objetos. Mientras que la herencia simple es más útil para definir objetos que son más

especializaciones de objetos generales.

Page 61: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

61

TEMA V

FUNCIONES VITUALES Y POLIMORFISMO

Introducción.-

El Polimorfismo (implementado en C++ con funciones virtuales) es la tercera característica

esencial de un lenguaje orientado a objetos, después de la abstracción de datos y la herencia.

De hecho, nos provee de otra dimensión para la separación entre interfaz y la implementación,

desacoplando el qué del cómo. El Polimorfismo permite mejorar la organización del código y su

legibilidad así como la creación de programas extensibles que pueden "crecer" no sólo durante

el desarrollo del proyecto, si no también cuando se deseen nuevas características.

La encapsulación crea nuevos tipos de datos combinando características y comportamientos. El

control de acceso separa la interfaz de la implementación haciendo privados (private) los

detalles. Estos tipos de organización son fácilmente entendibles por cualquiera que venga de la

programación procedimental. Pero las funciones virtuales tratan de desunir en términos de tipos.

En el Capítulo 14, usted vió como la herencia permitía tratar a un objeto como su propio tipo o

como a su tipo base. Esta habilidad es básica debido a que permite a diferentes tipos (derivados

del mismo tipo base) ser tratados como si fueran un único tipo, y un único trozo de código es

capaz de trabajar indistintamente con todos. Las funciones virtuales permiten a un tipo expresar

sus diferencias con respecto a otro similar si ambos han sido derivados del mismo tipo base.

Esta distinción se consigue modificando las conductas de las funciones a las que se puede

llamar a través de la clase base.

Page 62: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

62

En este capítulo aprenderá sobre las funciones virtuales, empezando con ejemplos simples que

le mostrará lo "desvirtual" del programa.

Ligadura.-

Todo el trabajo se realiza detrás del telón gracias al compilador, que instala los mecanismos

necesarios de la ligadura dinámica cuando se crean funciones virtuales. Debido a que los

programadores se suelen beneficiar de la comprensión del mecanismo de las funciones virtuales

en C++, esta sección mostrará la forma en que el compilador implementa este mecanismo.

La palabra reservada virtual le dice al compilador que no debe realizar ligadura estática. Al

contrario, debe instalar automáticamente todos los mecanismos necesarios para realizar la

ligadura dinámica. Esto significa que si se llama a play() para un objeto Brass a través una

dirección a la clase base Instrument, se usará la función apropiada.

Para que funcione, el compilador típico crea una única tabla (llamada VTABLE) por cada clase

que contenga funciones virtuales. El compilador coloca las direcciones de las funciones virtuales

de esa clase en concreto en la VTABLE. En cada clase con funciones virtuales el compilador

coloca de forma secreta un puntero llamado vpointer (de forma abreviada VPTR), que apunta a la

VTABLE de ese objeto. Cuando se hace una llamada a una función virtual a través de un puntero

a la clase base (es decir, cuando se hace una llamada usando el polimorfismo), el compilador

silenciosamente añade código para buscar el VPTR y así conseguir la dirección de la función en la

VTABLE, con lo que se llama a la función correcta y tiene lugar la ligadura dinámica.

Todo esto - establecer la VTABLE para cada clase, inicializar el VPTR, insertar código para la

llamada a la función virtual - sucede automáticamente sin que haya que preocuparse por ello. Con

las funciones virtuales, se llama a la función apropiada de un objeto, incluso aunque el compilador

no sepa el tipo exacto del objeto.

Almacenando información de tipo.-

Se puede ver que no hay almacenada información de tipo de forma explícita en ninguna de las

clases. Pero los ejemplos anteriores, y la simple lógica, dicen que debe existir algún tipo de

información almacenada en los objetos; de otra forma el tipo no podría ser establecido en tiempo

de ejecución. Es verdad, pero la información de tipo está oculta. Para verlo, aquí está un ejemplo

que muestra el tamaño de las clases que usan funciones virtuales comparadas con aquellas que

no las usan:

Page 63: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

63

//: C15:Sizes.cpp

// Object sizes with/without virtual functions

#include <iostream>

using namespace std;

class NoVirtual {

int a;

public:

void x() const {}

int i() const { return 1; }

};

class OneVirtual {

int a;

public:

virtual void x() const {}

int i() const { return 1; }

};

class TwoVirtuals {

int a;

public:

virtual void x() const {}

virtual int i() const { return 1; }

};

int main() {

cout << "int: " << sizeof(int) << endl;

cout << "NoVirtual: "

<< sizeof(NoVirtual) << endl;

cout << "void* : " << sizeof(void*) << endl;

cout << "OneVirtual: "

<< sizeof(OneVirtual) << endl;

cout << "TwoVirtuals: "

<< sizeof(TwoVirtuals) << endl;

} ///:~

Sin funciones virtuales el tamaño del objeto es exactamente el que se espera: el tamaño de un

único int. Con una única función virtual en OneVirtual, el tamaño del objeto es el tamaño de

NoVirtual más el tamaño de un puntero a void. Lo que implica que el compilador añade un único

puntero (el VPTR) en la estructura si se tienen una o más funciones virtuales. No hay diferencia de

Page 64: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

64

tamaño entre OneVirtual y TwoVirtuals. Esto es porque el VPTR apunta a una tabla con

direcciones de funciones. Se necesita sólo una tabla porque todas las direcciones de las funciones

virtuales están contenidas en esta tabla.

Este ejemplo requiere como mínimo un miembro de datos. Si no hubiera miembros de datos, el

compilador de C++ hubiera forzado a los objetos a ser de tamaño no nulo porque cada objeto

debe tener direcciones distintas (¿se imagina cómo indexar un array de objetos de tamaño nulo?).

Por esto se inserta en el objeto un miembro "falso" ya que de otra forma tendríá un tamaño nulo.

Cuando se inserta la información de tipo gracias a la palabra reservada virtual, ésta ocupa el lugar

del miembro "falso". Intente comentar el int a en todas las clases del ejemplo anterior para

comprobarlo.

Tipos de ligaduras.-

Ligadura Estática: Se produce antes de la ejecución (durante la compilación).

Ligadura Dinámica: La ligadura dinámica ocurre en la ejecución.

Funciones virtuales.-

Para que la ligadura dinámica tenga efecto en una función particular, C++ necesita que se use

la palabra reservada virtual cuando se declara la función en la clase base. La ligadura en

tiempo de ejecución funciona unícamente con las funciones virtual es, y sólo cuando se está

usando una dirección de la clase base donde exista la función virtual, aunque puede ser

definida también en una clase base anterior.

Para crear una función miembro como virtual, simplemente hay que preceder a la declaración

de la función con la palabra reservada virtual. Sólo la declaración necesita la palabra reservada

virtual, y no la definición. Si una función es declarada como virtual, en la clase base, será

entonces virtual en todas las clases derivadas. La redefinición de una función virtual en una

clase derivada se conoce como overriding.

Hay que hacer notar que sólo es necesario declarar la función como virtual en la clase base.

Todas las funciones de las clases derivadas que encajen con la declaración que esté en la

clase base serán llamadas usando el mecanismo virtual. Se puede usar la palabra reservada

virtual en las declaraciones de las clases derivadas (no hace ningún mal), pero es redundante

y puede causar confusión.

Para conseguir el comportamiento deseado de Instrument2.cpp, simplemente hay que añadir

la palabra reservada virtual en la clase base antes de play().

Page 65: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

65

//: C15:Instrument3.cpp

// Late binding with the virtual keyword

#include <iostream>

using namespace std;

enum note { middleC, Csharp, Cflat }; // Etc.

class Instrument {

public:

virtual void play(note) const {

cout << "Instrument::play" << endl;

}

};

// Wind objects are Instruments

// because they have the same interface:

class Wind : public Instrument {

public:

// Override interface function:

void play(note) const {

cout << "Wind::play" << endl;

}

};

void tune(Instrument& i) {

Page 66: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

66

// ...

i.play(middleC);

}

int main() {

Wind flute;

tune(flute); // Upcasting

} ///:~Instrument3.cpp

Este archivo es idéntico a Instrument2.cpp excepto por la adición de la palabra reservada

virtual y, sin embargo, el comportamiento es significativamente diferente: Ahora la salida es

Wind::play.

Extensibilidad

Con play() definido como virtual en la clase base, se pueden añadir tantos nuevos tipos como

se quiera sin cambiar la función play(). En un programa orientado a objetos bien diseñado, la

mayoría de las funciones seguirán el modelo de play() y se comunicarán únicamente a través

de la interfaz de la clase base. Las funciones que usen la interfaz de la clase base no

necesitarán ser cambiadas para soportar a las nuevas clases.

Aquí está el ejemplo de los instrumentos con más funciones virtuales y un mayor número de

nuevas clases, las cuales trabajan de manera correcta con la antigua (sin modificaciones)

función play():

//: C15:Instrument4.cpp

// Extensibility in OOP

#include <iostream>

using namespace std;

enum note { middleC, Csharp, Cflat }; // Etc.

Page 67: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

67

class Instrument {

public:

virtual void play(note) const {

cout << "Instrument::play" << endl;

}

virtual char* what() const {

return "Instrument";

}

// Assume this will modify the object:

virtual void adjust(int) {}

};

class Wind : public Instrument {

public:

void play(note) const {

cout << "Wind::play" << endl;

}

char* what() const { return "Wind"; }

void adjust(int) {}

};

class Percussion : public Instrument {

public:

void play(note) const {

cout << "Percussion::play" << endl;

}

Page 68: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

68

char* what() const { return "Percussion"; }

void adjust(int) {}

};

class Stringed : public Instrument {

public:

void play(note) const {

cout << "Stringed::play" << endl;

}

char* what() const { return "Stringed"; }

void adjust(int) {}

};

class Brass : public Wind {

public:

void play(note) const {

cout << "Brass::play" << endl;

}

char* what() const { return "Brass"; }

};

class Woodwind : public Wind {

public:

void play(note) const {

cout << "Woodwind::play" << endl;

}

char* what() const { return "Woodwind"; }

Page 69: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

69

};

// Identical function from before:

void tune(Instrument& i) {

// ...

i.play(middleC);

}

// New function:

void f(Instrument& i) { i.adjust(1); }

// Upcasting during array initialization:

Instrument* A[] = {

new Wind,

new Percussion,

new Stringed,

new Brass,

};

int main() {

Wind flute;

Percussion drum;

Stringed violin;

Brass flugelhorn;

Woodwind recorder;

tune(flute);

Page 70: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

70

tune(drum);

tune(violin);

tune(flugelhorn);

tune(recorder)

f(flugelhorn);

} ///:~Instrument4.cpp

Se puede ver que se ha añadido otro nivel de herencia debajo de Wind, pero el mecanismo

virtual funciona correctamente sin importar cuantos niveles haya. La función adjust() no está

redefinida (override) por Brass y Woodwind. Cuando esto ocurre, se usa la definición más

"cercana" en la jerarquía de herencia - el compilador garantiza que exista alguna definición

para una función virtual, por lo que nunca acabará en una llamada que no esté enlazada con el

cuerpo de una función (lo cual sería desatroso).

El array A[] contiene punteros a la clase base Instrument, lo que implica que durante el

proceso de inicialización del array habrá upcasting. Este array y la función f() serán usados en

posteriores discusiones.

En la llamada a tune(), el upcasting se realiza en cada tipo de objeto, haciendo que se obtenga

siempre el comportamiento deseado. Se puede describir como "enviar un mensaje a un objeto y

dejar al objeto que se preocupe sobre qué hacer con él". La función virtual es la lente a usar

cuando se está analizando un proyecto: ¿Dónde deben estar las clases base y cómo se desea

extender el programa? Sin embargo, incluso si no se descubre la interfaz apropiada para la

clase base y las funciones virtuales durante la creación del programa, a menudo se descubrirán

más tarde, incluso mucho más tarde cuando se desee ampliar o se vaya a hacer funciones de

mantenimiento en el programa. Esto no implica un error de análisis o de diseño; simplemente

significa que no se conocía o no se podía conocer toda la información al principio. Debido a la

fuerte modularización de C++, no es mucho problema que esto suceda porque los cambios que

se hagan en una parte del sistema no tienden a propagarse a otras partes como sucede en C.

Page 71: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

71

Polimorfismo.-

Otro concepto interesante, con Importantes aportaciones en áreas tales como la flexibilidad o la

legibilidad del software, es el de polimorfismo. Tras este termino, un tanto oscuro, subyace una

idea bastante simple. En su más amplia expresión, el polimorfismo puede definirse como:

"el mecanismo que permite definir e Invocar funciones idénticas en denominación e interfaz, pero

con implementaron diferente".

Esta definición introduce un aspecto muy importante del polimorfismo: la asociación, o vinculo,

entre cada llamada a una de estas funciones polimorfismo y la implementación concreta

finalmente invocada. Cuando este vinculo puede establecerse en tiempo de compilación, se suele

hablar de vinculación estatica (static binding). Por contra, cuando la implementación a emplear,

puede determinarse en tiempo de ejecución, el termino empleado es el de vinculación dinámica

(dynamic binding).

En C++, por ejemplo, la vinculación dinámica de las llamadas a funciones polimórficas (en C++

reciben el calificativo de funciones virtuales) se consigue en base a la posibilidad que ofrece este

lenguaje de utilizar un puntero a objetos de una clase como puntero a objetos de cualquiera de las

clases descendientes de la anterior. Así, cuando la llamada a una función virtual, definida en una

clase y en una o varias de sus descendientes, se realiza sobre un objeto que viene referenciado

mediante un puntero a la clase padre, el compilador es incapaz de determinar que implementación

debe asociar a la llamada, ya que desconoce cual será la clase del objeto en el momento de su

ejecución. Dicha determinación debe quedar aplazada, por tanto, hasta ese instante.

Como se puede observar, el concepto de polimorfismo en C++, y en general en casi todos los

lenguajes de programación basados en el paradigma de objeto, esta estrechamente ligado al

concepto de herencia, dado que las funciones polimórficas sólo pueden definirse entre clases que

guardan entre sí una relación de parentesco (clases con un antecesor común).

Aunque el concepto de polimorfismo es una de las principales innovaciones del desarrollo

orientado a objetos, posee antecedentes históricos en otros mecanismos más sencillos, como son

la conversión forzada (casting) y la sobrecarga de identificadores, ideados con el fin de introducir

un cierto grado de flexibilidad en el manejo de tipos de datos heterogéneos.

En el siguiente apartado se desarrolla un pequeño ejemplo de aplicación de ambos conceptos, en

el que quedan claramente de manifiesto las ventajas potenciales de una correcta utilización de los

mismos. Para su implementación se ha elegido el lenguaje C ++, fundamentalmente por dos

razones: #include <iostream.h> void show(int val) {

Page 72: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

72

cout <<" Es un entero :"<< val << '\n'; } void show(char *val) { cout <<"Es un carácter: "<< val << '\n'; } main() { show (42); show ("A");

show (452.2); }

TEMA VI

PLANTILLAS Y TRATAMIENTO DE EXCEPCIONES

Introducción.-

La herencia y la composición proporcionan una forma de retilizar código objeto. Las plantillas de

C++ proporcionan una manera de reutilizar el código fuente.

Aunque las plantillas (o templates) son una herramienta de programación de propósito general,

cuando fueron introducidos en el lenguaje, parecían oponerse al uso de las jerarquías de clases

contenedoras basadas en objetos (demostrado al final del Capítulo 15). Además, los

contenedores y algoritmos del C++ Standard están construidos exclusivamente con plantillas y

son relativamente fáciles de usar por el programador.

Este capítulo no sólo muestra los fundamentos de los templates, también es una introducción a

los contenedores, que son componentes fundamentales de la programación orientada a objetos

lo cual se evidencia a través de los contenedores de la librería estándar de C++. Se verá que

este libro ha estado usando ejemplos contenedores - Stash y Stack- para hacer más sencillo el

concepto de los contenedores; en este capítulo se sumará el concepto del iterator. Aunque los

contenedores son el ejemplo ideal para usarlos con las plantillas, en el Volumen 2 (que tiene un

capítulo con plantillas avanzadas) se aprenderá que también hay otros usos para los templates

Plantillas.-

Ahora surge un nuevo problema. Tenemos un IntStack, que maneja enteros. Pero queremos

una pila que maneje formas, o flotas de aviones, o plantas o cualquier otra cosa. Reinventar el

código fuente cada vez no parece una aproximación muy inteligente con un lenguaje que

propugna la reutilización. Debe haber un camino mejor.

Page 73: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

73

Hay tres técnicas para reutilizar código en esta situación: el modo de C, presentado aquí como

contraste; la aproximación de Smalltalk, que afectó de forma significativa a C++, y la

aproximación de C++: los templates.

La solución de C. Por supuesto hay que escapar de la aproximación de C porque es

desordenada y provoca errores, al mismo tiempo que no es nada elegante. En esta

aproximación, se copia el código de una Stack y se hacen modificaciones a mano,

introduciendo nuevos errores en el proceso. Esta no es una técnica muy productiva.

La solución de Smalltalk. Smalltalk (y Java siguiendo su ejemplo) optó por una solución

simple y directa: Se quiere reutilizar código, pues utilicese la herencia. Para

implementarlo, cada clase contenedora maneja elementos de una clase base genérica

llamada Object (similar al ejemplo del final del capítulo 15). Pero debido a que la librería

de Smalltalk es fundamental, no se puede crear una clase desde la nada. En su lugar,

siempre hay que heredar de una clase existente. Se encuentra una clase lo más cercana

posible a lo que se desea, se hereda de ella, y se hacen un par de cambios. Obviamente,

esto es un beneficio porque minimiza el trabajo (y explica porque se pierde un montón de

tiempo aprendiendo la librería antes de ser un programador efectivo en Smalltalk).

Pero también significa que todas las clases de Smalltalk acaban siendo parte de un único árbol

de herencia. Hay que heredar de una rama de este árbol cuando se está creando una nueva

clase. La mayoría del árbol ya esta allí (es la librería de clases de Smalltalk), y la raiz del árbol es

una clase llamada Object - la misma clase que los contenedores de Smalltalk manejan.

Es un truco ingenioso porque significa que cada clase en la jerarquía de herencia de Smalltalk (y

Java) se deriva de Object, por lo que cualquier clase puede ser almacenada en cualquier

contenedor (incluyendo a los propios contenedores). Este tipo de jerarquía de árbol única basada

en un tipo genérico fundamental (a menudo llamado Object, como también es el caso en Java)

es conocido como "jerarquía basada en objectos". Se puede haber oido este témino y asumido

que es un nuevo concepto fundamental de la POO, como el polimorfismo. Sin embargo,

simplemente se refiere a la raíz de la jerarquía como Object (o algún témino similar) y a

contenedores que almacenan Objects.

Debido a que la librería de clases de Smalltalk tenía mucha más experiencia e historia detrás de

la que tenía C++, y porque los compiladores de C++ originales no tenían librerías de clases

contenedoras, parecía una buena idea duplicar la librería de Smalltalk en C++. Esto se hizo

como experimento con una de las primeras implementaciónes de C++, y como representaba un

Page 74: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

74

significativo ahorro de código mucha gente empezo a usarlo. En el proceso de intentar usar las

clases contenedoras, descubrieron un problema.

El problema es que en Smalltalk (y en la mayoría de los lenguajes de POO que yo conozco),

todas las clases derivan automáticamente de la jerarquía única, pero esto no es cierto en C++.

Se puede tener una magnifica jerarquía basada en objetos con sus clases contenedoras, pero

entonces se compra un conjunto de clases de figuras, o de aviones de otro vendedor que no usa

esa jerarquía. (Esto se debe a que usar una jerarquía supone sobrecarga, rechazada por los

programadores de C). ¿Cómo se inserta un árbol de clases independientes en nuestra jerarquía?

El problema se parece a lo siguiente:

Contenedores.-

Debido a que C++ suporta múltiples jerarquías independientes, la jerarquía basada en objetos

de Smalltalk no funciona tan bien.

La solución parace obvia. Si se pueden tener múltiples jerarquías de herencia, entonces hay

que ser capaces de heredar de más de una clase: La herencia múltiple resuelve el problema.

Por lo que se puede hacer lo siguiente:

Herencia múltiple.-

Page 75: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

75

Ahora OShape tiene las características y el comportamiento de Shape, pero como también está

derivado de Object puede ser insertado en el contenedor. La herencia extra dada a OCircle,

OSquare, etc. es necesaria para que esas clases puedan hacer upcast hacia OShape y puedan

mantener el comportamiento correcto. Se puede ver como las cosas se están volviendo confusas

rápidamente.

Los vendedores de compiladores inventaron e incluyeron sus propias jerarquías y clases

contenedoras, muchas de las cuales han sido reemplazadas desde entonces por versiones de

templates.

La solución de la plantilla.-

Aunque una jerarquía basada en objetos con herencia múltiple es conceptualmente correcta, se

vuelve difícil de usar se demostró lo que se consideraba una alternativa preferible a la jerarquía

basada en objetos. Clases contenedoras que fueran creadas como grandes macros del

preprocesador con argumentos que pudieran ser sustituidos con el tipo deseado. Cuando se

quiera crear un contenedor que maneje un tipo en concreto, se hacen un par de llamadas a

macros.

Desafortunadamente, esta aproximación era confusa para toda la literatura existente de

Smalltalk y para la experiencia de programación, y era un poco inmanejable. Básicamente,

nadie la entendía.

Mientras tanto, Stroustrup y el equipo de C++ de los Laboratorios Bell habían modificado su

aproximación de las macros, simplificándola y moviéndola del dominio del preprocesador al

compilador. Este nuevo dispositivo de sustitución de código se conoce como template

(plantilla), y representa un modo completamente diferente de reutilizar el código. En vez de

reutilizar código objeto, como en la herencia y en la composición, un template reutiliza código

fuente. El contenedor no maneja una clase base genérica llamada Object, si no que gestiona

un parámetro no especificado. Cuando se usa un template, el parámetro es sustituido por el

compilador, parecido a la antigua aproximación de las macros, pero más claro y fácil de usar.

hora, en vez de preocuparse por la herencia o la composición cuando se quiera usar una clase

contenedora, se usa la versión en plantilla del contenedor y se crea una versión específica para

el problema, como lo siguiente:

Page 76: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

76

Figura: Contenedor de objetos

El compilador hace el trabajo por nosotros, y se obtiene el contenedor necesario para hacer el

trabajo, en vez de una jerarquía de herencia inmanejable. En C++, el template implementa el

concepto de tipo parametrizado. Otro beneficio de la aproximación de las plantillas es que el

programador novato que no tenga familiaridad o esté incómodo con la herencia puede usar las

clases contenedoras de manera adecuada (como se ha estado haciendo a lo largo del libro con

el vector).

Concepto de excepciones.-

Las excepciones son situaciones anómalas que requieren un tratamiento especial.

¡¡No tienen por qué ser errores!!

Si se consigue dominar su programación, la calidad de las aplicaciones que se desarrollen

aumentará considerablemente.

El funcionamiento general del mecanismo de lanzamiento y tratamiento de excepciones es el

siguiente:

Existe un método que invoca la ejecución de otro.

Este método más interno se encuentra en una situación que puede considerarse como

excepcional. Por lo tanto lanza una excepción.

En este momento termina la ejecución del método más interno y se retorna

inmediatamente al método llamador.

El método llamador debe capturar la excepción y la trata. Parte del tratamiento de la

excepción puede ser volver a lanzarla al método que invocó a este.

La correcta programación de excepciones significa diseñar los algoritmos pensando únicamente

en la forma habitual en la que deben ejecutarse, manejando las situaciones extraordinarias a

Page 77: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

77

parte. De esta manera se consigue un diseño mucho más estructurado, legible, robusto y fácil de

mantener.

Ejemplo.-

Cuando solicitamos memoria al sistema, lo habitual es que exista suficiente memoria disponible y

no haya ningún problema. Pero si queremos realizar una aplicación robusta deberemos de tener

en cuenta la eventualidad de que dicha memoria no se conceda, lo cual complica enormemente

un sencillo algoritmo.

Veamos una función escrita en C que simplemente intenta asignar memoria dinámica para tres

enteros:

void SinExcepciones (void)

{

int *p1, *p2, *p3;

p1 = (int*) malloc(sizeof(int));

if (p1 == NULL) {

printf("No hay suficiente memoria");

abort();

}

p2 = (int*) malloc(sizeof(int));

if (p2 == NULL) {

printf("No hay suficiente memoria");

abort();

}

p3 = (int*) malloc(sizeof(int));

if (p3 == NULL) {

printf("No hay suficiente memoria");

abort();

Page 78: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

78

}

}

Si programamos en C++ y hacemos uso de las excepciones:

void ConExcepciones (void) {

int *p1, *p2, *p3;

try {

p1 = new int; // Comportamiento normal.

p2 = new int;

p3 = new int;

}

catch(...){ // Comportamiento excepcional.

printf("No hay suficiente memoria");

abort();

}

}

El ANSI C++ especifica que si una instrucción new falla (no hay memoria disponible) debe lanzar

la excepción bad_alloc (definida en el fichero de cabecera new). Las instrucciones que pueden

provocar el lanzamiento de una excepción (la "zona crítica") se encierran en un bloque try. Si

alguna de las instrucciones del bloque try provocara una excepción el flujo del programa se dirige

al final de ese bloque, buscando un bloque catch que capture la excepción (si no lo encontrara, el

programa terminaría -ver sección 6.4-). En este caso, la instrucción catch(...) captura cualquier

excepción, y en particular, la excepción bad_alloc. El tratamiento que se efectúa es, en este caso,

mostrar un mensaje de error y terminar la ejecución del programa.

Lanzamiento de excepciones.-

El lanzamiento de una excepción se realiza llamando a la función throw(). Cuando se lanza una

excepción, en realidad lo que se hace es crear un objeto de la clase que se le indique a throw(), y

precisamente será dicho objeto la excepción en sí.

Suele ser muy útil crear clases de excepciones propias para controlar las situaciones anómalas de

nuestra aplicación. Por ejemplo,

Page 79: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

79

En ObjGraf.h:

//*************************************************/

// Definicion de la clase EFueraRango

// Clase de excepcion por entrar en "fuera de rango":

// salirse de los limites del PaintBox al calcular

// las nuevas coordenadas de dibujo de la pelota.

//*************************************************/

class EFueraRango {};

Con lo que podríamos lanzar una excepción de la siguiente manera:

throw EfueraRango();

Nota: Aunque las excepciones sean clases, en C++ Builder existe la convención de que su

nombre empiece por la letra E, y no por T como el resto de las clases.

Aunque en el ejemplo anterior la clase creada para la excepción, no tiene ningún miembro

(propiedades o métodos), se le pueden añadir. Éstos servirán para poder incorporar información

en el objeto excepción, acerca de la situación en la que se produjo la excepción, que podrá ser

utilizada por la sección de código que lo trate.

Por ejemplo, si tuviéramos un método que lanzara una excepción por falta de memoria, podría ser

interesante que incorporara una propiedad que indicara cual era el máximo de memoria disponible

cuando se lanzó la excepción.

Especificación de excepciones.-

C++ cuenta con una característica denominada especificación de excepciones, que sirve para

enumerar, en una declaración, las excepciones que puede lanzar un método. Si queremos que un

método pueda lanzar un determinado tipo de excepción deberemos especificarlo de esta manera.

En ObjGraf.h, en la clase TObjGraf:

//*************************************************/

// Definicion de la clase base TObjGraf

//*************************************************/

Page 80: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

80

class TObjGraf {

private:

int FX;

int FY;

void SetX (int _X) throw (EFueraRango); // Si hay problemas,crean un

void SetY (int _Y) throw (EFueraRango); // objeto de clase EFueraRango

virtual int GetAncho (void) = 0; // Metodo virtual puro

virtual int GetAlto (void) = 0; // Metodo virtual puro

...

En ObjGraf.cpp:

// Funciones de escritura de las propiedades virtuales X e Y

void TObjGraf :: SetX (int _X) throw (EFueraRango)

{

if (_X < 0) { // Coordenada negativa

FX = 0; // Ajustar al margen izquierdo

throw EFueraRango(); // Lanzar excepcion

}

else

if (_X > (PaintBox->Width - Ancho)) { // Demasiado alta

FX = PaintBox->Width - Ancho; // Ajustar al margen derecho

throw EFueraRango(); // Lanzar excepcion

}

else

FX = _X; // Correcto: escribir sin modificar

}

void TObjGraf :: SetY (int _Y) throw (EFueraRango)

{

if (_Y < 0) { // Coordenada negativa

FY = 0; // Ajustar al margen superior

throw EFueraRango(); // Lanzar excepcion

}

else

if (_Y > (PaintBox->Height - Alto)) { // Demasiado alta

FY = PaintBox->Height - Alto; // Ajustar al margen inferior

throw EFueraRango(); // Lanzar excepcion

Page 81: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

81

}

else

FY = _Y; // Correcto: escribir sin modificar

}

Captura de excepciones.-

El bloque susceptible de producir alguna excepción ("zona crítica") se encierra en un bloque try, la

captura (discriminación de la excepción que se ha producido) se efectúa en una instrucción catch

y su procesamiento se realiza a continuación, en el bloque catch.

try {

<bloque de instrucciones críticas>

}

catch (<tipo excepción1> <variable1>) {

<manejador 1>

}

catch (<tipo excepción2> <variable2>) {

...

}

Podemos especificar tantos bloques catch para un bloque try como deseemos, en el momento que

ocurra una excepción se ejecutará el bloque catch cuya clase concuerde con la de la excepción.

Si se especifica catch (...) se capturará cualquier excepción.

La excepción no tiene porqué lanzarse explícitamente en el bloque try sino que puede ser

consecuencia de la ejecución de una función que se ha llamado dentro de ese bloque.

En ObjGraf.cpp:

void TPelota :: Mover (void)

{

Borrar ();

try {

X += FDirX * Velocidad;

}

catch (EFueraRango) {

Page 82: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

82

FDirX = -FDirX;

};

try {

Y += FDirY * Velocidad;

}

catch (EFueraRango) {

FDirY = -FDirY;

};

Mostrar ();

}

En este ejemplo, la instrucción "crítica":

X += FDirX * Velocidad;

se traduce a:

X = X + FDirX * Velocidad;

y como X es una propiedad virtual, en realidad se trata de hacer:

SetX (FX + FDirX * Velocidad);

donde se observa que es el método SetX() el que puede provocar el lanzamiento de la excepción.

En Ppal.cpp:

1. Dejar únicamente la siguiente declaración global:

2. TPelota *Pelota;

3. Modificar las funciones FormCreate() y FormDestroy() para que queden:

4. //----------------------------------------------------------

5.

6. void __fastcall TPpalFrm::FormCreate(TObject *Sender)

7. {

8. Pelota = new TPelota (PaintBox, clYellow, 120, 70, 25);

9. }

10. //----------------------------------------------------------

11.

12. void __fastcall TPpalFrm::FormDestroy(TObject *Sender)

13. {

14. delete Pelota;

15. }

16. Eliminar el manejador del evento OnPaint del componente PaintBox (borrando su cuerpo solamente).

17. Añadir a PpalFrm un TTimer (Carpeta System) y fijar Interval=100. En el evento OnTimer poner:

18. //----------------------------------------------------------

Page 83: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

83

19.

20. void __fastcall TPpalFrm::Timer1Timer(TObject *Sender)

21. {

22. Pelota->Mover();

23. }

24. //---------------------------------------------------------

LECTURAS COPLEMENTARIAS

Disciplinas relacionadas con POA

Por último, hay muchas disciplinas y campos de investigación relacionados de alguna manera con

la orientación a aspectos, algunos persiguen los mismos objetivos y algunos otros. En este

apartado se hará mención a algunos de ellos, de manera que se tenga una visión más amplia de

por dónde se mueve la investigación en esta disciplina.

Reflexión y protocolos de meta objetos.-

Un sistema reflectivo proporciona un lenguaje base y uno o más metalenguajes que proporcionan

control sobre la semántica del lenguaje base y la implementación. Los metalenguajes

proporcionan vistas de la computación que ningún componente del lenguaje base puede percibir.

Los metalenguajes son de más bajo nivel que los lenguajes de aspectos en los que los puntos de

enlace son equivalentes a los “ganchos” de la programación reflectiva. Estos sistemas se pueden

utilizar como herramienta para la programación orientada a aspectos.

Transformación de programas.-

El objetivo que persigue la transformación de programas es similar al de la orientación a aspectos.

Busca el poder escribir programas correctos en un lenguaje demás alto nivel, y luego,

transformarlos de forma mecánica en otros que tengan un comportamiento idéntico, pero cuya

eficiencia sea mucho mejor. Con este estilo de programación algunas propiedades de las que el

programador quiere implementar se escriben en un programa inicial. Otras se añaden gracias a

que el programa inicial pasa a través de varios programas de transformación. Esta separación es

similar a la que se produce entre los componentes y los aspectos.

Programación subjetiva.-

La programación subjetiva y la orientada a aspectos son muy parecidas, pero no son exactamente

lo mismo. La programación subjetiva soporta la combinación automática de métodos para un

Page 84: UNIVERSIDAD SALESIANA DE BOLIVIA INGENIERIA DE SISTEMASvirtual.usalesiana.edu.bo/web/contenido/dossier/22011/667.pdf · las herramientas de software, lo cual facilitara la inserción

84

mensaje dado a partir de diferentes sujetos. Estos métodos serían equivalentes a los

componentes tal y como se perciben desde la POA .Hay una diferencia clave entre ambas

disciplinas, y es que mientras que los aspectos de la POA tienden a ser propiedades que afectan

al rendimiento o la semántica de los componentes, los sujetos de la programación subjetiva son

características adicionales que se añaden a otros sujetos.

BIBLIOGRAFIA

AUTOR OBRA LUGAR DE EDICIÓN

EDITORIAL AÑO

JOYANES AGUILAR, LUIS

““PPRROOGGRRAAMMAACCIIOONN

OORRIIEENNTTAADDAA AA OOBBJJEETTOOSS””

MADRID

MGGRAW-HILL

1998

JOYANES AGUILAR, LUIS

““FFUUNNDDAAMMEENNTTOOSS DDEE LLAA

PPRROOGGRRAAMMAACCIIOONN ““

MADRID MGGRAW-HILL

2003

BOOCH, GRADY ““DDIISSEENNOO OORRIIEENNTTAADDOO AA

OOBBJJEETTOOSS CCOONN

AAPPLLIICCAACCIIOONNEESS””

MADRID MGGRAW-HILL

1991

STAUGUARD,ANDREW ““TTEECCNNIICCAASS

EESSTTRRUUCCTTUURRAADDAASS YY

OORRIIEENNTTAADDAASS AA

OOBBJJEETTOOSS::UUNNAA

IINNTTRROODDUUCCCCIIOONN

UUTTIILLIIZZAANNDDOO CC++++””

NEW YORK

ADDISON-WESLEY

1998

SCHILDT,HERBERT ““AAPPLLIIQQUUEE TTUURRBBOO CC++++”” MADRID MGGRAW-HILL

1991

DEITEL Y DEITEL ““CCOOMMOO PPRROOGGRRAAMMAARR EENN

JJAAVVAA”” MADRID MGGRAW-

HILL 2005