13.01.2015 Views

Pensar en C++ (Volumen 1) - Grupo ARCO

Pensar en C++ (Volumen 1) - Grupo ARCO

Pensar en C++ (Volumen 1) - Grupo ARCO

SHOW MORE
SHOW LESS

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 />

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

Saved successfully!

Ooh no, something went wrong!