INSTITUTO TECNOLOGICO de la laguna Programación Orientada a Objetos en C++



Documentos relacionados
7.- Seleccionando funciones friend o funciones miembro para sobrecarga de operadores.

Tema: Sobrecarga de Operadores.

Modulo 1 El lenguaje Java

Tema 3: Herencia en C++ Programación Orientada a Objetos Curso 2008/2009 Begoña Moros Valle

CONTENIDOS. 1. Completar el ejemplo de Herencia: Superclase Persona-Subclase Alumno

Prof. Dr. Paul Bustamante

Índice ÍNDICE EJERCICIO 1: CÁLCULO FINANCIERO (5 PTOS.) EJERCICIO 2: AGENCIA DE COLOCACIONES (5 PTOS.)...4

LABORATORIO Nº 2 GUÍA PARA REALIZAR FORMULAS EN EXCEL

En cualquier caso, tampoco es demasiado importante el significado de la "B", si es que lo tiene, lo interesante realmente es el algoritmo.

GUIA PROGRAMACIÓN ORIENTADA A OBJETOS

Tema: Arreglos de Objetos en C++.

Capítulo 6. Introducción a la POO

Centro de Capacitación en Informática

Tema 6. Reutilización de código. Programación Programación - Tema 6: Reutilización de código

Patrones para persistencia (I) Ingeniería del Software II

Plantillas de clases ( Templates )

Conceptos. ELO329: Diseño y Programación Orientados a Objetos. ELO 329: Diseño y Programación Orientados a Objetos

Aprendiendo a programar Microcontroladores PIC en Lenguaje C con CCS

EXAMEN FINAL Metodología y Programación Orientada a Objetos. Curso Cuatrimestre de otoño. 17 de Enero de 2011

Apuntes de Matemática Discreta 1. Conjuntos y Subconjuntos

Introduccion al Lenguaje C. Omar Andrés Zapata Mesa Grupo de Fenomenología de Interacciones Fundamentales, (Gfif) Universidad de Antioquia

Guía Corta: Alcance y Asociaciones. 1. Preliminares: Nombres y Asociaciones

11. Algunas clases estándar de Java (II)

Matrices Invertibles y Elementos de Álgebra Matricial

Herencia. 3.- Herencia. Declaración de una clase derivada en Delphi. Jerarquía de clases

Lección 24: Lenguaje algebraico y sustituciones

Examen Junio- Grupo A Lunes 17 de Junio - Programación en C++ Pág. 1

Este programa mueve cada motor de forma independiente, y cuando termina una línea pasa a la siguiente.

LEER Y ESCRIBIR ARCHIVOS O FICHEROS EN C. FOPEN, FCLOSE, MODOS DE ACCESO READ, WRITE Y APPEND (CU00536F)

Lección 4: Suma y resta de números racionales

Prof. Dr. Paul Bustamante

ISTP CIDET COMPUTACION E INFORMATICA ARREGLOS EN JAVA

TABLA DE DECISION. Consideremos la siguiente tabla, expresada en forma genérica, como ejemplo y establezcamos la manera en que debe leerse.

A25. Informática aplicada a la gestión Curso 2005/2006 Excel Tema 7. Funciones avanzadas de Excel II

Árboles AVL. Laboratorio de Programación II

El palacio de la Alhambra: La primera expansión. El favor de los visires

PHP y MySQL. Inicio: - Herencia - Palabra clave Final - Polimorfismo - Type Hinting - Abstracción de clases

Curso de Doctorado: Tecnologías de Objetos

CASO PRÁCTICO DISTRIBUCIÓN DE COSTES

Ahora comencemos!... Las operaciones matemáticas fundamentales pueden realizarse de forma rápida y sencilla con Miicrosofftt Excell.

TEMA 5. CONTROL DE FLUJO DEL PROGRAMA. Sentencia Instrucción Expresión Operadores + Operandos Sintaxis: Sentencia ;

Constructores y Destructores

TEMA 3. EL PROCESO DE COMPILACIÓN, DEL CÓDIGO FUENTE AL CÓDIGO MÁQUINA

MATERIAL 2 EXCEL 2007

UNIDAD 6. POLINOMIOS CON COEFICIENTES ENTEROS

Preliminares. Tipos de variables y Expresiones

LABORATORIO Nº 3 PRÁCTICA DE FUNCIONES EN MICROSOFT EXCEL

Las propiedades de la clase en java es el equivalente a las variables globales en lenguajes estructurados como el C.

Contenidos. Funciones (suplemento) Funciones. Justificación del uso de Funciones

CLASE # 5 TÉCNICAS DE CAJA BLANCA

Introducción. Ciclo de vida de los Sistemas de Información. Diseño Conceptual

Sesión 3 - Movimiento Diferencial

Centro de Capacitación en Informática

Introducción a la programación orientada a objetos

Manual de usuario Software PC Editor de Rutas. inled

El número de arriba de la fracción, el numerador, nos dice cuántas de las partes iguales están coloreadas.

Figura 4.1 Clasificación de los lenguajes de bases de datos

Tema: Patrones de Diseño.

Dividir automáticamente las palabras en todo un documento

Programación Orientada a Objetos con Java

GENERAR DOCUMENTOS HTML USANDO LENGUAJE PHP. EJERCICIO RESUELTO EJEMPLO SENCILLO. (CU00733B)

= x + x + x + 1 por definición de exponente 2

ESCUELA DE INGENIERÍA DE SISTEMAS DEPARTAMENTO DE COMPUTACIÓN PROGRAMACIÓN 2 PRÁCTICA DE LABORATORIO 7 Herencia y Composición en POO

INSTITUTO POLITÉCNICO NACIONAL ESCUELA SUPERIOR DE INGENIERÍA MECÁNICA Y ELÉCTRICA UNIDAD CULHUACÁN INTEGRANTES

Dra. Carmen Ivelisse Santiago Rivera 1 MÓDULO DE LOS ENTEROS. Por profesoras: Iris Mercado y Carmen Ivelisse Santiago GUÍA DE AUTO-AYUDA

Base de datos relacional

LABORATORIO 1 OPERACIONES DE ENTRADA Y SALIDA

Java Inicial (20 horas)

Ecuaciones de primer grado con dos incógnitas

Operaciones con polinomios

Informática I Notas del curso

EDWIN KÄMMERER ORCASITA INGENIERO ELECTRÓNICO

RESUMEN DE CONCEPTOS BASICOS DE PROGRAMACION JAVA

Módulo II - PowerPoint

DIAGRAMA DE CLASES EN UML

1. Ejemplo de clase : La clase Cuenta 2. Uso de la clase Cuenta. 3. Métodos y objetos receptores de mensajes (Importante)

Semana Empecemos! Qué sabes de...? El reto es... Vamos al grano. Excel. Parte II

CONSULTAS MULTITABLAS SQL SERVER Manual de Referencia para usuarios. Salomón Ccance CCANCE WEBSITE

LENGUAJES DE CONSULTA ORIENTADOS A OBJETOS

Universidad Católica del Maule. Fundamentos de Computación Especificación de tipos de datos ESPECIFICACIÓN ALGEBRAICA DE TIPOS DE DATOS

Programación Orientada a Objetos en Java

NÚMEROS NATURALES Y NÚMEROS ENTEROS

Introducción a la Programación Orientada a Objetos

Soporte lógico de computadoras

2. Conceptos básicos Abstracción La abstracción como un proceso mental natural La abstracción en el desarrollo de software

Contenidos. Gestión dinámica de memoria. Gestión dinámica de memoria. Introducción. 1. Introducción 2. El operador NEW 3. El operador DELETE

3.2 Operaciones aritmético-lógicas en Pascal

15. Parámetros o argumentos

Ejemplos de conversión de reales a enteros

Lección 1-Introducción a los Polinomios y Suma y Resta de Polinomios. Dra. Noemí L. Ruiz Limardo 2009

ESPOCH ESCUELA DE MEDICINA HERNANDEZ MAYRA FORMULAS Y DUNCIONES BASICAS ESPOCH

15 CORREO WEB CORREO WEB

35 Facultad de Ciencias Universidad de Los Andes Mérida-Venezuela. Potencial Eléctrico

INTRODUCCIÓN A LA CONTABILIDAD DE COSTOS DEFINICIÓN

Universidad de Cantabria

Transcripción completa de la Lección 2 (Pinyin).

Tecnólogo Informático- Estructuras de Datos y Algoritmos- 2009

Fundamentos de Programción (I)

Creación de Funciones de Conducción

INTRODUCCIÓN AL TIPO COMPUESTO CADENA CONTENIDOS

Transcripción:

5.- Herencia Múltiple. Un hecho natural es que una persona tenga más de un pariente mayor, esta situación también se puede dar en la herencia de clases, naturalmente este tipo de herencia involucra un mayor grado de complejidad en el lenguaje, sin embargo, el beneficio logrado es substancial. Considere por ejemplo, la clase llamada MesaRedonda la cual tiene las propiedades de una Mesa y también tiene las propiedades de un Circulo, de acuedo a la siguiente estructura. Circulo radio : real = initval Circulo() area() Mesa altura : real = initval Mesa() alto() MesaRedonda color : entero = initval MesaRedonda() Color() Veamos ahora la estructura de clases en C++. // Programa PLCP84.CPP // Herencia Múltiple. #include <math.h> #include <stdio.h> class Circulo float radio; Circulo(float r) radio = r; float area() return M_PI*radio*radio; class Mesa float altura; Mesa(float a) altura = a; float alto() return altura; Jove - 356 -

class MesaRedonda : public Mesa, public Circulo int color; MesaRedonda(float r, float a, int c); int Color() return color; MesaRedonda::MesaRedonda(float r, float a, int c) : Circulo(r), Mesa(a) color = c; MesaRedonda mesa(15.0, 3.0, 5); printf("\nlas propiedades de la mesa son:"); printf("\naltura = %0.2f", mesa.alto()); printf("\narea = %0.2f", mesa.area()); printf("\ncolor = %d", mesa.color()); OBSERVACIONES. La salida nos resulta: Las propiedades de la mesa son: Altura = 3.00 Area = 706.86 Color = 5 En este caso podemos observar que la clase MesaRedonda hereda las propiedades tanto de la clase Circulo como de la clase Mesa, ésto nos reduce bastante la declaración de la clase MesaRedonda. Podemos observar en la declaración de la clase MesaRedonda que no solo hay que declarar de que clases hereda características la clase derivada, también es necesario declarar las especificaciones de acceso de cada clase base. Estas especificaciones se usan de la misma manera que en la herencia simple, y no tienen que ser las mismas para todas las clases base. En el ejemplo anterior, en la función main(), son invocadas tres funciones miembro desde l clase MesaRedonda, estas son; MesaRedonda::Alto(), MesaRedonda::area() y MesaRedonda::Color(), note que en ningún caso se indica de que clase son heredadas dichas funciones, y cuales no lo son, todas se tratan como si Jove - 357 -

fueran funciones miembro de la clase MesaRedonda. Esta forma de referirse a miembros heredados de superclases es una extensión natural de la herencia simple. 5.1) Declarando una clase derivada con multiples clases base La declaración de la clase MesaRedonda nos muestra la sintaxis usada; la lista de clases base se indica después del nombre de la clase derivada y de 2 puntos (:), con su especificador de acceso y separada cada clase por una coma (,). Estas clases base son llamadas clases base directas, para distinguirlas de las clases base indirectas, las cuales son clases base de una clase base directa (clases abuelas, diriamos). La clase Mesa y Circulo son clases base directas de la clase derivada MesaRedonda, pero si las clases Mesa y Circulo tuvieran alguna clase base, éstas se llamarían clases base indirectas de la clase MesaRedonda. Esta distinción entre clases base directa e indirecta es importante, ya que el compilador aplica diferentes reglas para cada tipo. Una clase puede tener tantas clases base como se quiera, pero ésto nos incrementa la complejidad. Como un punto de referencia podemos tomar la implementación de las librerias de C++, en donde, cuando se usa herencia multiple se usan a lo más dos clases base. 5.2) Invocando a los constructores de la clase base. De la misma forma que con herencia simple, los constructores en multiples clases base son invocados antes del constructor de la clase derivada. El orden de declaración especifica el orden de invocación. Por ejemplo, consideremos el caso para la clase MesaRedonda. class MesaRedonda : public Mesa, public Circulo int color; MesaRedonda(float r, float a, int c); int Color() return color; Cuando se hace la instanciación de esta clase, el orden de llamada de los constructores de las clases base es el siguiente: Mesa::Mesa(float r); Circulo::Circulo(float a); MesaRedonda::MesaRedonda(int c); Jove - 358 -

En la definición del constructor de la clase MesaRedonda, se pasaron 2 argumentos a las clases base (r, a). MesaRedonda::MesaRedonda(float r, float a, int c) : Circulo(r), Mesa(a) color = c; El orden de llamada a estos constructores puede causar confusión, ya que el orden de llamada es definido en la declaración de la clase, NO en la definición del constructor, donde el orden de especificación de los constructores no importa, el orden especificado de llamada a los constructores de las clases base es aparente. En el caso de definir constructores virtuales, estos son llamados antes de cualquier constructor no virtual. 5.3) Usando Clases Base Virtuales. Las clases Base Virtuales solo se usan en el contexto de herencia múltiplo. Dada la complejidad que puede aparecer en las relaciones de clase, en la definición del árbol de herencia múltilpe, hay situaciones en las cuales el programador requiere contar con un cierto nivel de control en la forma en que las clases base heredan sus atributos. Considere, por ejemplo, el siguiente árbol de herencia. A valor : entero = initval A valor : entero = initval B x : entero = initval C y : entero = initval D valor() Jove - 359 -

La clase D tiene a la clase A como una clase base, el problema es que existen 2 clases A diferentes que aparecen como clases base de la clase D, donde cada una de las cuales tiene sus propios datos, aunque en realidad es solo una clase A, pero al considerarla como clase base de 2 clases derivadas diferentes, el compilador maneja 2 copias de la clase A. En la vida real, esta situación será equivalente a que una persona tenga 2 abuelos, los cuales no solo son genelos, sino también tienen el mismo nombre. Cómo lidear con esta situación?. Consideremos el siguiente código: // Programa PLCP85.CPP // Herencia múltiple, clase repetidas #include <iostream.h> class A int valor; class B : public A int x; class C : public A int y; class D : public B, public C int Valor() return valor; D d; d.c::valor = 5; D *dp = new D; int w = dp -> B::valor = 10; cout << "Valor de v = " << d.valor() << endl; cout << "Valor de w = " << w << endl; delete dp; OBSERVACIONES. Jove - 360 -

En este caso la sentencia de acceso al dato miembro en la clase D es ambigua. Borland C++ nos genera el siguiente mensaje de error. "Member is ambiguous in 'A::valor' and 'A::valor' in function D::Valor()" El compilador no sabe a que copia de valor se esta haciendo referencia, entonces, deberemos aplicar el operador de resolución de ámbito en la función de la clase D::Valor(). int Valor() C::valor; Una función fuera de la clase D podrá también accesar cada uno de los datos miembro usando el operador de resolución de ámbito combinado con un identificador del objeto. NOTA: En el programa anterior aparece también una advertencia, por el aparente no uso del objeto d en la función main(). El código a continuación nos muestra una solución al problema, de acuerdo como se planteó anteriormente. // Programa PLCP85.CPP // Herencia múltiple, clase repetidas #include <iostream.h> class A int valor; class B : public A int x; class C : public A int y; class D : public B, public C int Valor() return C::valor; Jove - 361 -

D d; d.c::valor = 5; D *dp = new D; int w = dp -> B::valor = 10; cout << "Valor de v = " << d.valor() << endl; cout << "Valor de w = " << w << endl; delete dp; OBSERVACIONES. NOTA: En este programa ya no se genera ningún error ni advertencia. En la clase D se hace una clara distinción de la rama de donde viene la clase base A, ésto es, se hace la distinción que viene por el lado de la superclase C. También es necesario indicarlo cuando se inicializa el dato miembro valor. Si se usa la clase base B o se usa la clase base C. Si se usa la clase base C, y a esta copia de valor se le asigna un 5, como se definió en la función int D::Valor() que se esta derivando de la clase C, es posible usar esta función para mostrar el dato asignado. Esta estructura es más o menos compleja, se puede simplificar un poco redefiniendo la estructura de clase como sigue. A valor : entero = initval B x : entero = initval C y : entero = initval D valor() Jove - 362 -

El tener múltiples copias de la misma clase base, en un árbol de herencia no es solo confuso sino también involucra un derroche de memoria. Declarando la clase base A como virtual resuelve este problema, forzando al compilador a manejar solo una copia de la clase base en las declaraciones de las clases derivadas. Esto nos lleva también a la redefinición de la función de Valor() de la clase D. El código para esta estructura lo escribimos de la forma: // Programa PLCP85A.CPP // Herencia múltiple, clase repetidas #include <iostream.h> class A int valor; class B : public virtual A int x; class C : public virtual A int y; class D : public B, public C int Valor() return valor; D d; d.valor = 5; D *dp = new D; int w = dp -> valor = 10; cout << "Valor de v = " << d.valor() << endl; cout << "Valor de w = " << w << endl; delete dp; Jove - 363 -

OBSERVACIONES. Al haber definido como virtual la clase base A, tanto en la declaración de la clase B como en la declaración de la clase C, se establece la estructura de clases arriba indicada, y la estructura del programa vuelve a la normalidad, note que en este caso por simplicidad se manejaron los datos miembro como públicos. 5.4) Usando clases virtuales y no virtuales juntas. Una clase puede tener clases base virtuales y no virtuales en cualquier orden y combinación. Si tenemos el siguiente árbol de clases. A valor : entero = initval A valor : entero = initval B x : entero = initval C y : entero = initval D valor() E z : entero = initval El código para dicho árbol de clases resulta de la siguiente forma: class A class B : public virtual A Jove - 364 -

class C : public virtual A class D : public A class E : public B, public C, public D La clase E maneja 2 diferentes copias de la clase A. Este hecho no genera un error en C++, pero puede causar confusión en los usuarios. De acuerdo a la declaración de la clase E, el orden de llamada de los constructores es el siguiente: A, B, C, D, E. Primero se construye la clase base virtual, en seguida, las demás clases base. El orden en que las clases virtuales son construidas corresponde al orden de aparición en la declaración de la clase. Después que todas las clases base virtuales son construidas, se llaman a los constructores de las clases no virtuales, en el orden en que aparecen en la declaración de la clase. 5.5) Invocando a los destructores. Un destructor solo puede invocarse de manera indirecta, por medio del operador delete. La secuencia de llamada de los destructores, es el inverso en que fueron llamados los constructores. 5.6) Usando conversión de tipos. Cuando se usa herencia simple, se puede convertir, por ejemplo, un puntero de una clase derivada en un puntero de una clase base, recuerde que lo contrario no es posible. Con herencia múltiple, las cosas son más dificiles, la conversión de punteros y referencias no siempre es posible, ya que se puede presentar ambigüedad. Consideremos nuestro primer ejemplo de herencia múltiple. // Programa PLCP85B.CPP // Herencia múltiple, clase repetidas, conversión de tipos. #include <iostream.h> class A int valor; class B : public A int x; class C : public A Jove - 365 -

int y; class D : public B, public C int Valor() return C::valor; /* ---------------------------------------- GENERA ERROR A *pa1 = new D; No se puede hacer int w = pa1 -> valor = 10; esta conversión. ---------------------------------------- */ A *pa2 = new B; int x = pa2 -> valor = 20; A *pa3 = new C; int y = pa3 -> valor = 30; // cout << "Valor de w = " << w << endl; GENERA ERROR cout << "Valor de x = " << x << endl; cout << "Valor de y = " << y << endl; // delete pa1; GENERA EEROR delete pa2; delete pa3; OBSERVACIONES. Cuando el compilador ve la sentencia: A *pa1 = new D; El compilador se da cuenta que la clase D tiene 2 diferentes versiones de la clase A, declarando la clase A como virtual, se puede prevenir este problema, ya que entonces, solo una copia de la clase A es incluida en la clase D. Veamos como resulta el código. // Programa PLCP85C.CPP // Herencia múltiple, clase repetidas, conversión de tipos. // uso de clases base virtuales. #include <iostream.h> Jove - 366 -

class A int valor; class B : public virtual A int x; class C : public virtual A int y; class D : public B, public C int Valor() return valor; A *pa1 = new D; pa1 -> valor = 10; A *pa2 = new B; pa2 -> valor = 20; A *pa3 = new C; pa3 -> valor = 30; cout << "Valor de pa1 -> valor = " << pa1 -> valor << endl; cout << "Valor de pa2 -> valor = " << pa2 -> valor << endl; cout << "Valor de pa3 -> valor = " << pa3 -> valor << endl; delete pa1; delete pa2; delete pa3; OBSERVACIONES. Ahora ya no hay ningún problema ya que solo existe una copia de la clase base A. La salida nos resulta: Jove - 367 -

Valor de pa1 -> valor = 10 Valor de pa2 -> valor = 20 Valor de pa3 -> valor = 30 5.7) Búsqueda de una clase en el árbol de clases. Cuando es invocada una función dentro de una clase derivada, el compilador busca en el árbol de clases, aquellas a las que se tiene acceso, siguiendo ciertas reglas. Si la función no es encontrada en la clase desde donde es invocada, el compilador la busca hacia arriba a través del árbol de herencia. Esto sucede en tiempo de compilación, no en tiempo de ejecución. Si varias clases tienen una función con el mismo nombre, C++, de acuerdo a ciertas reglas determina cual función esta siendo llamada. Considere el siguiente árbol de herencia. A fun c() B func() C x : entero = initval A continuación se presenta la implementación del árbol de herencia anterior.. // Programa PLCP86.CPP // Trayectoria de busqueda de funciones // en el árbol de clases. #include <iostream.h> Jove - 368 -

class A int func() return 1; class B : virtual public A int func() return 2; class C : public virtual A, public B int x; C c; int i = c.func(); cout << "i = " << i << endl; // Llama a B::func(); int j = c.a::func(); cout << "j = " << j << endl; // Llama a A::func(); OBSERVACIONES. En este caso el compilador sigue la regla de dominación. El compilador se da cuenta que en ambas clases B y A, se encuentra una función llamada func(). La regla de dominación le dice que si 2 clases contienen la función buscada, y si una clase es derivada de la otra, la clase derivada domina. En el ejemplo anterior la clase B satisface este criterio, si no existe una función dominante, el compilador genera un error de ambigüedad. En el siguiente árbol se presenta este error de ambigüedad, ya que no hay una clase dominante. Jove - 369 -

A B func() func() C x : entero = initval D y : entero = initval Su codificación resulta: // Programa PLCP86A.CPP // Ambigüedad en la regla de dominación #include <iostream.h> class A func() return 1; class B func() return 2; class C : public B int x; Jove - 370 -

class D : public C, public A int y; D d; int i = d.func(); cout << "i = " << i << endl; OBSERVACIONES. Al compilar el programa anterior el compilador nos va a generar el siguiente error. "Member is ambiguous; 'B::func' and 'A::func' en function main()" 5.8) Usando el operador de resolución de ámbito con herencia múltiple. El error en el programa anterior se puede corregir re-escribiendo main() para incluir el operador de resolución de ámbito. D d; int i = d.a::func(); int j = d.b::func(); cout << "i = " << i << endl; cout << "j = " << j << endl; La corrección de main(), se hace inclusive para la llamada a las 2 funciones miembro func(). Esto es, usando el operador de resolución de ámbito para especificar de manera explicita al compilador que función deseamos llamar, ya que en este caso, no puede aplicar la regla de dominación. Jove - 371 -

La declaración de varias clases base con el mismo identificador público, no ocasiona un error. Esto lo permite el compilador al declarar los identificadores, sin embargo si se puede producir un problema al accesarlos. El acceso a los identificadores se determina solo cuando el código ejecutable necesita ser generado, entonces solo se puede usar un identificador a la vez. Por ejemplo, suponga que un identificador es declarado y éste puede ser ambiguo en el árbol de herencia, en estos casos, el error es detectado solamente si el programador escribe código que intente accesar el identificador ambiguo, sin el uso del operador de resolución de ámbito apropiado. Jove - 372 -