10.07.2015 Views

10.- Constructores Virtuales. El C++ no se permite el uso de ...

10.- Constructores Virtuales. El C++ no se permite el uso de ...

10.- Constructores Virtuales. El C++ no se permite el uso de ...

SHOW MORE
SHOW LESS
  • No tags were found...

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

INSTITUTO TECNOLOGICO<strong>de</strong> la lagunaProgramación Orientada a Objetos en <strong>C++</strong><strong>10.</strong>- <strong>Constructores</strong> <strong>Virtuales</strong>.<strong>El</strong> <strong>C++</strong> <strong>no</strong> <strong>se</strong> <strong>permite</strong> <strong>el</strong> <strong>uso</strong> <strong>de</strong> constructores virtuales. Esta limitación esimpuesta más bien por dificulta<strong>de</strong>s técnicas <strong>de</strong> implementación que por dificulta<strong>de</strong>s <strong>de</strong>concepto. <strong>El</strong> problema es este; La invocación <strong>de</strong> una función virtual requiere <strong>de</strong> la<strong>de</strong>finición <strong>de</strong> un vptr en la tabla <strong>de</strong> apuntadores virtuales vtab. Sin embargo, antes <strong>de</strong>que un objeto <strong>se</strong>a construido en memoria <strong>no</strong> existe tal tabla. Por otro lado la i<strong>de</strong>a <strong>de</strong>constructores virtuales es confusa, pero tal vez <strong>se</strong>a necesario que figure en <strong>el</strong> lenguaje<strong>de</strong> alguna manera, por lo pronto, <strong>no</strong> esta disponible.11.- Destructores virtuales.<strong>C++</strong> <strong>permite</strong> la <strong>de</strong>claración <strong>de</strong> un <strong>de</strong>structor virtual. Esto es porque un <strong>de</strong>structores invocado justo antes <strong>de</strong> que un objeto <strong>de</strong>saparesca <strong>de</strong> memoria, <strong>el</strong> vptr va a estardisponible para su <strong>uso</strong>. Hay que hacer <strong>no</strong>tar que <strong>el</strong> vtab es <strong>de</strong>finido por cla<strong>se</strong>, <strong>no</strong> porobjeto, por lo tanto vtab va a estar pre<strong>se</strong>nte <strong>de</strong>spués <strong>de</strong> <strong>se</strong>r <strong>de</strong>struido un objeto, pero<strong>no</strong> hay acceso a él. Por otro lado, como un <strong>de</strong>structor <strong>no</strong> pue<strong>de</strong> tener argumentos, solo<strong>se</strong> pue<strong>de</strong> <strong>de</strong>clarar virtual un <strong>de</strong>structor por cla<strong>se</strong>.12.- Un ejemplo <strong>de</strong> Polimorfismo.<strong>El</strong> polimorfismo <strong>no</strong> solo hace más simples los programas, también los hace másflexibles y robustos. En esta <strong>se</strong>cción <strong>se</strong> va a pre<strong>se</strong>ntar un programa <strong>el</strong> cual usafunciones virtuales, lo que <strong>no</strong>s va a permitir ilustrar la potencia d<strong>el</strong> polimorfismo.Consi<strong>de</strong>re una <strong>se</strong>rie <strong>de</strong> cla<strong>se</strong>s usadas para la implementación <strong>de</strong> una ba<strong>se</strong> <strong>de</strong>datos. Las cla<strong>se</strong>s <strong>se</strong> organizan en una jerarquía, con la intención <strong>de</strong> permitir alprogramador accesar cualquier objeto en la ba<strong>se</strong> <strong>de</strong> datos, sin co<strong>no</strong>cer todos losobjetos involucrados.Cualquier acceso a la ba<strong>se</strong> <strong>de</strong> datos inicia en <strong>el</strong> <strong>no</strong>do más alto en <strong>el</strong> árbol. Cada cantoen la ba<strong>se</strong> <strong>de</strong> datos tiene un autor y una fecha. Otra información pue<strong>de</strong> variar a través<strong>de</strong> las cla<strong>se</strong>s, por ejemplo, <strong>el</strong> método usado para mostrar información acerca <strong>de</strong> u<strong>no</strong>bjeto. Esta función muestra(), es una buena candidata para aplicar polimorfismo, por lotanto <strong>se</strong> implementa usando <strong>el</strong> mecanismo virtual.Jove - 392 -


INSTITUTO TECNOLOGICO<strong>de</strong> la lagunaProgramación Orientada a Objetos en <strong>C++</strong>A continuación <strong>se</strong> pre<strong>se</strong>nta <strong>el</strong> árbol <strong>de</strong> herencia.Objeto_DBautor : char *titulo : char *fecha : char *Objeto_DB()virtual muestra()Pinturaancho : intalto : intPintura()virtual muestra()Musicakey : char *Musica()virtual muestra()Recintonum_mus : intRecinto()virtual muestra()// Programa PLCP95.H// Un ejemplo <strong>de</strong> polimorfismo.#ifn<strong>de</strong>f DB_H_#<strong>de</strong>fine DB_H_#inclu<strong>de</strong> #inclu<strong>de</strong> class Objeto_DB {char autor[50];char titulo[50];char fecha[50];public:Objeto_DB(char *, char *, char *);virtual void muestra();};Jove - 393 -


INSTITUTO TECNOLOGICO<strong>de</strong> la lagunaProgramación Orientada a Objetos en <strong>C++</strong>class pintura : public Objeto_DB {int ancho;int alto;public:pintura(char *, char *, char *, int, int);virtual void muestra();};class musica : public Objeto_DB {public:musica(char *, char *, char *, char *);virtual void muestra();};class recinto : public musica {int num_mus;public:recinto(char *, char *, char *, char *, int);virtual void muestra();};#endif // Cla<strong>se</strong> DB_H_A continuación <strong>se</strong> pre<strong>se</strong>nta la implementación, en un archivodiferente.// Programa PLCP95.CPP// Implementación <strong>de</strong> la cla<strong>se</strong> Objeto_DB#inclu<strong>de</strong> "PLCP95.H"Objeto_DB::Objeto_DB(char *quien, char *que, char *cuando){strcpy(autor, quien);strcpy(titulo, que);strcpy(fecha, cuando);}void Objeto_DB::muestra(){printf("\nAutor : %s", autor);printf("\nTitulo : %s", titulo);printf("\nFecha : %s", fecha);}Jove - 394 -


INSTITUTO TECNOLOGICO<strong>de</strong> la lagunaProgramación Orientada a Objetos en <strong>C++</strong>pintura::pintura(char *autor, char *titulo, char *fecha,int w, int h) : Objeto_DB(autor, titulo, fecha){ancho = w;alto = h;}void pintura::muestra(){Objeto_DB::muestra();printf("\nTipo : Pintura");printf("\nTamaño : Ancho = %d, Altura = %d",ancho, alto);}musica::musica(char *autor, char *titulo, char *fecha, char *k): Objeto_DB(autor, titulo, fecha){strcpy(key, k);}void musica::muestra(){Objeto_DB::muestra();printf("\nTipo : Musica");printf("\nLlave : %s", key);}recinto::recinto(char *autor, char *titulo, char *fecha,char *key, int tam) : musica(autor, titulo,fecha, key){num_mus = tam;}void recinto::muestra(){musica::muestra();printf("\nOtro : Recinto <strong>de</strong> música %d músicos", num_mus);}Jove - 395 -


INSTITUTO TECNOLOGICO<strong>de</strong> la lagunaProgramación Orientada a Objetos en <strong>C++</strong>Consi<strong>de</strong>remos <strong>el</strong> caso en que d <strong>se</strong> refiere a un objeto d<strong>el</strong> tipo musica. La funciónmusic::muestra() sobre-escribe la función <strong>de</strong> la cla<strong>se</strong> ba<strong>se</strong> Objeto_DB::muestra(), con<strong>el</strong>lo, la tabla <strong>de</strong> funciones viertuales convierte la llamada a la función virtualObjeto_DB::muestra(), en la llamada a la función virtual musica::muestra(). La cualaparece <strong>de</strong>ntro d<strong>el</strong> cuerpo <strong>de</strong> la función musica::muestra(). Esto <strong>de</strong>berá causar un cicloinfinito, ya que la llamada a la función Objeto_DB::muestra() va a causar <strong>de</strong> nuevo lallamada a la función musica::muestra(). Al usar <strong>el</strong> operador <strong>de</strong> resolución <strong>de</strong> ámbito,forza al compilador a generar en tiempo <strong>de</strong> compilación la llamada a la función, con locual <strong>se</strong> salta <strong>el</strong> mecanismo virtual, que ocurre en tiempo <strong>de</strong> ejecución. Evitando<strong>se</strong>entrar en un ciclo infinito <strong>de</strong> llamadas.14.- Uso <strong>de</strong> funciones virtuales con funciones NO virtuales.Usando funciones virtuales y funciones <strong>no</strong> virtuales juntas en una jerarquía <strong>de</strong>cla<strong>se</strong>s, pue<strong>de</strong> causar confusión <strong>de</strong> manera inicial para <strong>el</strong> <strong>no</strong>vicio. Para ejemplificar loanterior, consi<strong>de</strong>re <strong>el</strong> siguiente árbol <strong>de</strong> herencia.Una herencia <strong>de</strong> cla<strong>se</strong>s usando funciones virtuales y <strong>no</strong> virtuales juntas.u<strong>no</strong>a : intvirtual fun1()u<strong>no</strong>()ge()pone()dosvirtual fun1()ge()dos()<strong>El</strong> árbol <strong>de</strong> cla<strong>se</strong>s anterior, lo implementamos como sigue:// Programa PLCP96.CPPJove - 398 -


INSTITUTO TECNOLOGICO<strong>de</strong> la lagunaProgramación Orientada a Objetos en <strong>C++</strong>#inclu<strong>de</strong> class u<strong>no</strong> {int a;public:u<strong>no</strong>(int x = 0) { a = x; }virtual void fun1() {cout


INSTITUTO TECNOLOGICO<strong>de</strong> la lagunaProgramación Orientada a Objetos en <strong>C++</strong>La salida <strong>no</strong>s resulta:Función u<strong>no</strong>::fun1(), a = 4Función u<strong>no</strong>::ge()Función dos::fun1(), a = 6, b = 8Función u<strong>no</strong>::ge()Po<strong>de</strong>mos ob<strong>se</strong>rvar que en <strong>el</strong> caso <strong>de</strong> la función virtual <strong>se</strong> inicia la trayectoria enla cla<strong>se</strong> ba<strong>se</strong> u<strong>no</strong>, hasta la cla<strong>se</strong> <strong>de</strong>rivada, por <strong>el</strong>lo, al llamar a un objeto <strong>de</strong> la cla<strong>se</strong>u<strong>no</strong>, <strong>se</strong> llama a la función virtual <strong>de</strong> la cla<strong>se</strong> u<strong>no</strong>, y al llamar a un objeto <strong>de</strong> la cla<strong>se</strong> dosinicia buscando la función virtual a partir <strong>de</strong> la cla<strong>se</strong> ba<strong>se</strong>, hasta la cla<strong>se</strong> <strong>de</strong>rivada, yejecuta esta verisión <strong>de</strong> la función.En <strong>el</strong> caso <strong>de</strong> llamar a una función <strong>no</strong> virtual, como <strong>el</strong> parámetro <strong>de</strong> la funciónfun_u<strong>no</strong>() es d<strong>el</strong> tipo <strong>de</strong> la cla<strong>se</strong> ba<strong>se</strong>, <strong>no</strong> hay una trayectoria a <strong>se</strong>guir, entonces, llama<strong>de</strong> manera inmediata a la función <strong>de</strong>finida en la cla<strong>se</strong> ba<strong>se</strong>.En <strong>el</strong> siguiente ejemplo <strong>se</strong> muestra una forma <strong>de</strong> accesar a la función dos::ge().// Programa PLCP96A.CPP#inclu<strong>de</strong> class u<strong>no</strong> {int a;public:u<strong>no</strong>(int x = 0) { a = x; }virtual void fun1() {cout


INSTITUTO TECNOLOGICO<strong>de</strong> la lagunaProgramación Orientada a Objetos en <strong>C++</strong>};void ge() {cout


INSTITUTO TECNOLOGICO<strong>de</strong> la lagunaProgramación Orientada a Objetos en <strong>C++</strong>15.- Una vista en memoria <strong>de</strong> las estructuras vptr y vtab.Es fácil enten<strong>de</strong>r como <strong>el</strong> compilador localiza las funciones virtuales en tiempo <strong>de</strong>ejecución consi<strong>de</strong>rando la estructura <strong>de</strong> la memoria usada en los objetos <strong>de</strong> las cla<strong>se</strong>sque involucran funciones virtuales.En <strong>el</strong> ejemplo anterior, la llamada a la función fun_u<strong>no</strong>(u<strong>no</strong>&), mediante u<strong>no</strong>bjeto <strong>de</strong> la cla<strong>se</strong> dos, resultó en la llamada a las funciones dos::fun1() y u<strong>no</strong>::ge().Esto suce<strong>de</strong> ya que en la llamada a la función:fun_u<strong>no</strong>(B)la referencia a dos& es convertido <strong>de</strong> manera implicita a u<strong>no</strong>&, entonces, <strong>el</strong> compilador<strong>se</strong> da cuenta que u<strong>no</strong>::ge() <strong>no</strong> es una función virtual, por lo tanto, genera código parallamar a la función u<strong>no</strong>::ge() directamente. La llamada a la función u<strong>no</strong>::fun1() en lafunción fun_u<strong>no</strong>(u<strong>no</strong>&) <strong>se</strong> maneja <strong>de</strong> manera diferente ya que <strong>el</strong> compilador <strong>se</strong> dacuenta que la función fun1() es <strong>de</strong>clarada como virtual en la cla<strong>se</strong> u<strong>no</strong>, lo cual provocala generación <strong>de</strong> código para <strong>el</strong> <strong>uso</strong> d<strong>el</strong> mecanismo virtual. Las dos funciones virtualesson puestas en memoria como sigue:En tiempo <strong>de</strong> ejecución, <strong>el</strong> vector vptr indica a que función <strong>se</strong> tiene acceso,<strong>de</strong>pendiendo d<strong>el</strong> tipo <strong>de</strong> objeto pasado como argumento. En <strong>el</strong> caso <strong>de</strong> <strong>se</strong>r, porejemplo, un objeto <strong>de</strong> la cla<strong>se</strong> dos, la función a <strong>se</strong>r llamada, consi<strong>de</strong>rando la funciónvirtual fun1() <strong>de</strong> la cla<strong>se</strong> anterior, <strong>se</strong>rá la función: dos::fun1(). <strong>El</strong> vector vptr, <strong>de</strong> la cla<strong>se</strong>dos <strong>se</strong> almacena en <strong>el</strong> mismo off<strong>se</strong>t que <strong>el</strong> vptr <strong>de</strong> la cla<strong>se</strong> u<strong>no</strong>, entonces, la funciónfun_u<strong>no</strong>(u<strong>no</strong>&) tiene acceso a una función virtual <strong>de</strong>pendiendo d<strong>el</strong> tipo <strong>de</strong> objeto queJove - 402 -


INSTITUTO TECNOLOGICO<strong>de</strong> la lagunaProgramación Orientada a Objetos en <strong>C++</strong><strong>se</strong>a pasado como argumento a dicha función, Note sin embargo, que los vptr's para lacla<strong>se</strong> u<strong>no</strong> y para la cla<strong>se</strong> dos manejan diferentes tablas, vtab.Si ob<strong>se</strong>rvamos la pila mediante un <strong>de</strong>purador, veremos lo siguiente para <strong>el</strong>programa anterior:------------------------------------------------------------------------------------------------------------off<strong>se</strong>t obj direc. cont. vtab Dir. ini. fun. virtuales------------------------------------------------------------------------------------------------------------2 FFF4 00040 A FFF2 010F DS:010F CS:0463 u<strong>no</strong>::fun1()4 FFF0 00082 FFEE 00060 B FFEC 010D DS:010D CS:040F dos::fun1()------------------------------------------------------------------------------------------------------------Si ob<strong>se</strong>rvamos <strong>el</strong> código, vemos que las funciones virtuales son llamadasmediante la instrucción: call [BX]. Lo que significa que la dirección <strong>de</strong> ejecución pue<strong>de</strong><strong>se</strong>r manejada durante la ejecución d<strong>el</strong> programa, y <strong>no</strong> necesariamente en lacompilación.Jove - 403 -

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!