Diferencia entre revisiones de «Programación en C++/Funciones virtuales»
Contenido eliminado Contenido añadido
Ortografía Etiqueta: editor de código 2017 |
|||
Línea 49:
En este ejemplo, 'p' puede ser usado para acceder a todos los
elementos de objD heredados de objB. Sin embargo elementos
a objD no pueden ser referenciados con 'p'.
Para un ejemplo mas concreto, considere el siguiente programa, el cual
define una clase llamada clase_B y una clase derivada llamada clase_D.
Este programa usa una simple
títulos.
<source lang="cpp">
Línea 135:
clase_D y puede ser usado para acceder aquellos elementos de la clase
derivada que son heredados de la clase base. Pero recuerde, un puntero
base no puede acceder aquellos elementos
derivada. De
puntero dp, el cual es un puntero a la clase derivada.
Línea 142:
usando un puntero de clase base, se debe hacer un casting hacia el
puntero del tipo derivado. Por ejemplo, esta linea de codigo llamara
apropiadamente a la
<source lang="cpp">
Línea 149:
Los
no con el tipo de retorno de mostrar_titulo(). Aunque no hay nada
probablemente mejor evitarlo, ya que este simplemente agrega
a sus
consideran esto como mala forma.)
Línea 162:
Un puntero es incrementado y decrementado relativamente a su tipo base.
Por lo tanto, cuando un puntero de la clase base
un objeto derivado, incrementarlo o decrementarlo no
siguiente objeto de la clase derivada. En vez de eso, apuntara a
( lo que piense que es ) el
tanto,
puntero de clase base cuando esta apuntando a un objeto derivado.
El hecho de que un puntero a un tipo base pueda ser usado para apuntar
a cualquier objeto derivado de la base es extremadamente importante,
y fundamental para C++. Como
es crucial para la manera en que C++ implementa su polimorfismo en
tiempo de
=== REFERENCIAS A TIPOS DERIVADOS ===
Línea 179:
Similar a la acción de punteros ya descritas, una referencia a la
clase base puede ser usada para referirse a un objeto de un tipo
derivado. La
base puede recibir objetos de la clase base
tipo derivado de esa misma base.
=== FUNCIONES VIRTUALES ===
El polimorfismo en tiempo de
de dos
sobre la herencia en el
Una función virtual es una
una clase base y es redefinida en una o
cada clase derivada puede tener su propia
virtual. Lo que hace interesantes a las funciones virtuales es que
sucede cuando una es llamada a
( o referencia ). En esta
la
puntero. Y, esta
de la
de objeto al que
que determina
diferentes clases son derivadas de esa clase base, entonces cuando
tipos diferentes de objetos
puntero de clase base, diferentes versiones de la
son ejecutadas. Lo mismo ocurre cuando se usa una referencia a la
clase base.
Se declara una
su
virtual es redefinida por una clase derivada, la palabra clave
'virtual' no necesita ser repetida ( aunque no es un error hacerlo ).
Una clase que incluya una
'clase
hereda una clase base conteniendo una
Examine este corto programa, el cual demuestra el uso de funciones
Línea 277:
Examinemos el programa en detalle para comprender como funciona:
En 'base', la
significa que la
Dentro de ambas 'primera_d' y 'segunda_d', 'quien()' es redefinida
relativa a cada clase. Dentro de main(), cuatro variables son
declaradas: 'obj_base', el cual es un objeto del tipo 'base'; 'p'
el cual un un puntero a objetos del tipo 'base'; 'obj_primera' y
'obj_segunda', que son objetos de dos clases derivadas. A
'p' es asignada con la
es llamada. Como quien() es declarada como virtual, C++ determina
en tiempo de
tipo del objeto apuntado por 'p'. En este caso, 'p' apunta a un objeto
del tipo 'base',
'base' la que es ejecutada. A
puede referirse a un objeto de cualquier clase derivadas. Ahora,
cuando quien() es llamada, C++ nuevamente comprueba para ver que tipo
de objeto es apuntado por 'p' y basado en su tipo, determina cual
'obj_primera', esa
cuando 'p' es asignada con la
de quien() declarada en 'segunda_d' es ejecutada.
RECUERDE: "Es determinado en tiempo de
basada solamente en el tipo del objeto que
un puntero de clase base."
Una
del operador
ejemplo precedente, no
quien() usando esta
<source lang="cpp">
Línea 314:
</source>
Sin embargo, llamar a una
atributos
por un puntero de clase base que el polimorfismo en tiempo de
A primera vista, la
derivada parece ser una forma especial de sobrecarga de
embargo, este no es el caso. De hecho, los dos procesos son
fundamentalmente diferentes. Primero, una
diferir en su tipo y/o
virtual redefinida debe tener exactamente el mismo tipo y
de
redefiniciones debe ser exactamente los mismos. Si los prototipos
difieren, entonces la
y su naturaleza virtual se pierde. Otra
virtual debe ser un miembro, no una
para la cual es definida. Sin embargo, una
ser una
funciones destructores ser virtuales, pero esto no es
los constructores.
Línea 347:
Por las restricciones y diferencias entre sobrecargar funciones
normales y redefinir funciones virtuales, el
es usado para describir la
=== LAS FUNCIONES VIRTUALES SON HEREDADAS ===
Una vez que una
virtual sin importar cuantas capas de clases derivadas esta debe
perdurar. Por ejemplo, si 'segunda_d' es derivada de 'primera_d'
en vez de 'base', como se muestra en el
quien() es aun virtual y la
seleccionada.
Línea 369:
</source>
Cuando una clase derivada no redefine una
la
intente esta
no redefine 'quien()':
Línea 429:
Como confirma la salida, como 'quien()' no ha sido redefinida por
'segunda_d', entonces 'p' apunta a 'obj_segunda', es la
'quien()' en 'base' la que es ejecutada.
Mantenga en mente que las
son
para que 'segunda_d' sea derivada de 'primera_d' en vez de 'base',
entonces cuando quien() es referenciada relativa a un objeto del tipo
'segunda_d', es la
primera_d' la que es llamada ya que es la clase mas cercana a
'segunda_d', no 'quien()' dentro de base. El siguiente programa
demuestra esta
<source lang="cpp">
Línea 495:
</pre>
Como puede ver, 'segunda_d' ahora usa la
'primera_d' porque esa
herencia.
=== PORQUE FUNCIONES VIRTUALES ===
Como se declaraba en el inicio de este capítulo, las funciones virtuales en combinación con tipos derivados le permiten a C++ soportar polimorfismo en tiempo de ejecución. El polimorfismo es esencial para la programación orientada a objetos por una razón: Esta permite a una clase generalizada especificar aquellas funciones que serán comunes a todas las derivadas de esa clase, mientras que permite a una clase derivada definir la implementación específica de algunas o todas de esas funciones. A veces esta idea es expresada como: La clase base dicta la 'interface' general que cualquier objeto derivado de esa clase tendrá, pero permite a la clase derivada definir el método actual usado para implementar esa
Parte del truco de aplicar el polimorfismo de una manera satisfactoria es comprender que la clase base y derivada forman una jerarquía, la cual se mueve de mayor a menor generalización (base a derivada). Diseñada correctamente, la clase base provee todos los elementos que una clase derivada puede usar directamente. También define cuales funciones la clase derivada debe implementar por su cuenta. Esto permite a la clase derivada la flexibilidad para definir sus propios métodos, y aun mantener un interface consistente. Eso es, como la forma de la interface es definida por la clase base, cualquier clase derivada compartirá esa interface común. Además, el uso de funciones virtuales hace posible para la clase base definir interfaces genéricas que serán usada por todas las clases derivadas.
En este punto, usted debe preguntarse a si mismo porque una consistente interface con múltiples implementaciones es importante. La respuesta, nuevamente, no lleva a la fuerza central manejadora detrás de la programación orientada a objetos: Esta ayuda al programador a manejar programas de complejidad creciente. Por ejemplo, si usted desarrolla su programa correctamente, entonces usted sabrá que todos los objetos que usted derive de una clase base son accedidos en la misma manera general, incluso si las acciones específicas varían de una clase derivada a la próxima. Esto significa que usted necesita solo recordar una interface, en vez de varias. También, su clase derivada es libre de usar cualquiera o toda la funcionalidad provista por la clase base. No necesita reinventa esos elementos. Por tanto, la separación de interface e implementación permite la creación de librerías de clases, las cuales pueden ser provistas por un tercero. Si estas librerías son implementadas correctamente, ellas proveerán una interface común que usted puede usar para derivar clases suyas propias que cumplan sus necesidades
=== UNA SIMPLE
Para tener una idea del poder del concepto "una interface,
llamada 'figura'. Esta clase almacena las dimensiones de varios
objetos de 2-dimensiones y calcula sus
es una
a las clases derivadas. Sin embargo, 'mostrar_area()' es declarada
como virtual porque el
puede variar. El programa usa 'figura' para derivar dos clases
<source lang="cpp">
Línea 580:
La salida es mostrada
<pre>
Línea 587:
</pre>
En el programa,
'triangulo', son la misma, incluso ambas proveen sus propios
para computar el
Dada la
llamada 'circulo' que
La respuesta es 'Si'. Todo lo que necesita hacer es crear un nuevo
tipo derivado que calcule el
funciones virtuales
con otros objetos relaciones. Por ejemplo, esta es una manera de
hacerlo:
Línea 614:
</source>
Antes de intentar usar 'circulo', vea de cerca la
'mostrar_area()'. Note que esta usa solo el valor de x, el cual se
asume que almacena el radio. ( Recuerde, el
calculada usando la
'set_dim()' como se define en 'figura', asume que
dos valores, no solo uno. Como '
valor, que tipo de acción podemos tomar?
Hay dos manera de resolver este problema. La primera y peor, usted
simplemente llama a 'set_dim()' usando un valor falso como segundo
de ser chapucero, en conjunto requiriendo que recuerde una
especial, la cual viola la
Una mejor manera de resolver este problema es pasarle al
dentro de 'set_dim()' un valor por defecto. Entonces, cuando se llame
a 'set_dim()' para un
Cuando llame a 'set_dim()' para un
Línea 720:
</pre>
TIP: Mientras que las funciones virtuales son
de comprender, su verdadero poder no puede ser demostrado en ejemplos
cortos. En general, el polimorfismo logra su gran fuerza en sistemas
largos y complejos. Si continua usando C++, las oportunidades de
aplicar funciones virtuales se
=== FUNCIONES VIRTUALES PURAS Y CLASES ABSTRACTAS ===
Como se ha visto, cuando una
en una clase derivada es llamada por un objeto de esa clase derivada,
la
utilizada. Sin embargo, en muchas circunstancias, no
clase base. Por ejemplo, en la clase base 'figura' usada en el ejemplo
anterior, la
sustituto
tipo de objeto. Como
no es poco
sin significado en el contexto de su clase base.
Cuando esta
Una manera, como se muestra en el ejemplo, es simplemente hacer que
la
ejemplo, puede haber funciones virtuales que simplemente deben ser
definidas por la clase derivada para que la clase derivada tenga
significado si 'mostrar_area()' no se encuentra definida. En este
caso, usted desea algun metodo para asegurarse de que una clase
derivada, de hecho, defina todas las funciones necesarias. En
C++, la
Una '
que no tiene
tipo derivado debe definir su propia
no puede usar la
virtual pura use esta forma general:
Línea 763:
es el nombre de la
como pura. Por ejemplo, la siguiente
'mostrar_area()' es una
Línea 782:
</source>
Declarando una
derivada a definir su propia implementación. Si una clase falla en
hacerlo, el compilador
compilar esta
la definición de mostrar_area() ha sido removida de la clase
'
<source lang="cpp">
Línea 859:
</source>
Si una clase tiene al menos una
esa clase se dice que es 'abstracta'. Una clase abstracta tiene una
En vez de eso, una clase abstracta debe ser usada solo como una base
que otras clases
no puede ser usada para declarar un objeto es, por supuesto, que una
o
si la clase base es abstracta, la puede usar
punteros o referencias, los cuales son necesarios para soportar el
polimorfismo en tiempo de
=== ENLACE TEMPRANO VS
Existen dos
sobre programación orientada a objetos: "Enlace temprano y Enlace
C++, estos
compilacion y eventos que ocurren en tiempo de ejecucion,
respectivamente.
Enlace temprano significa que una llamada a una
en tiempo de
llamar a una
Ejemplos de enlace temprano incluyen llamadas a funciones
llamadas a funciones sobrecargadas y llamadas a funciones de
operadores sobrecargados. La principal ventaja del enlace temprano es
la eficiencia -- es
desventaja es falta de flexibilidad.
Enlace
tiempo de ejecución.
es determinada "al vuelo" mientras el programa se ejecuta. Enlace
tipos derivados. La ventaja del enlace
flexibilidad. Puede ser usada para soportar una interface común,
mientras que se permite a varios objetos utilizar esa interface para
definir sus propias implementaciones. Por tanto, puede ser usada para
ayudar a crear
y extendidas. Su desventaja, sin embargo, es una ligera
velocidad de
=== POLIMORFISMO Y EL PURISTA ===
A
hecho una
tiempo de
compilacion son sobrecarga de operadores y funciones. El polimorfismo
en tiempo de
Sin embargo,
"polimorfismo".
Algunos puristas POO han insistido que el
ellos
polimorfismo. Parte de este punto de vista
de que los primeros lenguajes de computación polimórficos fueran
intérpretes (en el que todos los eventos
ejecución). La llegada de lenguajes polimórficos compilador expandió
el concepto de polimorfismo. Sin embargo,
término polimorfismo debería referirse solo a eventos en tiempo de
ejecución. La mayoría de los programadores de C++ no
con este punto de vista y establecen que el término aplica en ambos
casos a características en tiempo de compilación y en tiempo de
ejecución. Sin embargo, no se sorprenda si algun dia, alguien va en
contra de usted sobre el uso de este
Si su programa usa enlace
programa este diseñado para hacer. ( En realidad, la
programas grandes usan una
una de las características
precio que usted paga por este poder es que su programa se
ligeramente
cuando este le agregue significado a la estructura y manejabilidad
de su programa. ( En esencia, use -- pero no abuse -- el poder.)
Mantenga en mente, sin embargo, que la
por enlace
requiera enlace
</div>
|