Pensar en C++ (Volumen 1) - Grupo ARCO
Pensar en C++ (Volumen 1) - Grupo ARCO
Pensar en C++ (Volumen 1) - Grupo ARCO
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
✐<br />
✐<br />
✐<br />
“Volum<strong>en</strong>1” — 2012/1/12 — 13:52 — page 462 — #500<br />
✐<br />
Capítulo 15. Polimorfismo y Funciones virtuales<br />
el compilador g<strong>en</strong>era código para ese constructor, se g<strong>en</strong>era código para un constructor<br />
de esa clase, no para la clase base, ni para una clase derivada (debido a que una<br />
clase no puede saber quién la hereda). Por eso, el VPTR que use debe apuntar a la<br />
VTABLE de esa clase. El VPTR permanece inicializado a la VTABLE para el resto de<br />
vida del objeto a m<strong>en</strong>os que no sea la última llamada al constructor. Si posteriorm<strong>en</strong>te<br />
se llama a un constructor de una clase derivada, éste constructor pone el VPTR a<br />
su VTABLE, y así hasta que el último constructor termine. El estado del VPTR es determinado<br />
por el constructor que sea llamado <strong>en</strong> último lugar. Otra razón por la que<br />
los constructores son llamados <strong>en</strong> ord<strong>en</strong> desde la base al más derivado.<br />
Pero mi<strong>en</strong>tras que toda esta serie de llamadas al constructor ti<strong>en</strong>e lugar, cada<br />
constructor ha puesto el VPTR a su propia VTABLE. Si se usa el mecanismo virtual<br />
para llamar a funciones, producirá sólo una llamada a través de su propia VTABLE,<br />
y no de la VTABLE del más derivado (como debería suceder después de que todos<br />
los constructores hayan sido llamados). Además, muchos compiladores reconoc<strong>en</strong><br />
cuando se hace una llamada a una función virtual d<strong>en</strong>tro de un constructor, y realizan<br />
una ligadura estática porque sab<strong>en</strong> que la ligadura dinámica producirá una<br />
llamada a una función local. En todo caso, no se conseguirán los resultados que se<br />
podían esperar inicialm<strong>en</strong>te de la llamada a una función virtual d<strong>en</strong>tro de un constructor.<br />
15.10.3. Destructores y destructores virtuales<br />
No se puede usar la palabra reservada virtual con los constructores, pero los<br />
destructores pued<strong>en</strong>, y a m<strong>en</strong>udo deb<strong>en</strong>, ser virtuales.<br />
El constructor ti<strong>en</strong>e el trabajo especial de iniciar un objeto poco a poco, primero<br />
llamando al constructor base y después a los constructores derivados <strong>en</strong> el ord<strong>en</strong> de<br />
la her<strong>en</strong>cia. De manera similar, el destructor ti<strong>en</strong>e otro trabajo especial: desmontar un<br />
objeto, el cual puede pert<strong>en</strong>ecer a una jerarquía de clases. Para hacerlo, el compilador<br />
g<strong>en</strong>era código que llama a todos los destructores, pero <strong>en</strong> el ord<strong>en</strong> inverso al que<br />
son llamados <strong>en</strong> los constructores. Es decir, el constructor empieza <strong>en</strong> la clase más<br />
derivada y termina <strong>en</strong> la clase base. ésta es la opción deseable y segura debido a que<br />
el destructor siempre sabe que los miembros de la clase base están vivos y activos.<br />
Si se necesita llamar a una función miembro de la clase base d<strong>en</strong>tro del destructor,<br />
será seguro hacerlo. De esta forma, el destructor puede realizar su propio limpiado,<br />
y <strong>en</strong>tonces llamar al sigui<strong>en</strong>te destructor, el cual hará su propio limpiado, etc. Cada<br />
destructor sabe de que clase deriva, pero no cuales derivan de él.<br />
Hay que t<strong>en</strong>er <strong>en</strong> cu<strong>en</strong>ta que los constructores y los destructores son los únicos<br />
lugares donde ti<strong>en</strong>e que funcionar ésta jerarquía de llamadas (que es automáticam<strong>en</strong>te<br />
g<strong>en</strong>erada por el compilador). En el resto de las funciones, sólo esa función,<br />
sea o no virtual, será llamada (y no las versiones de la clase base). La única forma<br />
para acceder a las versiones de la clase base de una función consiste <strong>en</strong> llamar de<br />
forma explicita a esa funciones.<br />
Normalm<strong>en</strong>te, la acción del destructor es adecuada. Pero ¿qué ocurre si se quiere<br />
manipular un objeto a través de un puntero a su clase base (es decir, manipular al objeto<br />
a través de su interfaz g<strong>en</strong>érica) Este tipo de actividades es uno de los objetivos<br />
de la programación ori<strong>en</strong>tada a objetos. El problema vi<strong>en</strong>e cuando se quiere hacer<br />
un delete (eliminar) de un puntero a un objeto que ha sido creado <strong>en</strong> el montón<br />
(>heap) con new. Si el puntero apunta a la clase base, el compilador sólo puede conocer<br />
la versión del destructor que se <strong>en</strong>cu<strong>en</strong>tre <strong>en</strong> la clase base durante el delete.<br />
¿Su<strong>en</strong>a familiar Al fin y al cabo, es el mismo problema por las que fueron creadas<br />
las funciones virtuales <strong>en</strong> el caso g<strong>en</strong>eral. Afortunadam<strong>en</strong>te, las funciones virtuales<br />
462<br />
✐<br />
✐<br />
✐<br />
✐