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 456 — #494<br />

✐<br />

Capítulo 15. Polimorfismo y Funciones virtuales<br />

int main() {<br />

Pet p("Alfred");<br />

Dog d("Fluffy", "sleep");<br />

describe(p);<br />

describe(d);<br />

} ///:~<br />

La función describe() recibe un objeto de tipo Pet por valor. Después llama a<br />

la función virtual description() del objeto Pet. En el main(), se puede esperar<br />

que la primera llamada produzca "This is Alfred", y que la segunda produzca "Fluffy<br />

likes to sleep". De hecho, ambas usan la versión description() de la clase base.<br />

En este programa están sucedi<strong>en</strong>do dos cosas. Primero, debido a que describe-<br />

() acepta un objeto Pet (<strong>en</strong> vez de un puntero o una refer<strong>en</strong>cia), cualquier llamada<br />

a describe() creará un objeto del tamaño de Pet que será puesto <strong>en</strong> la pila y<br />

posteriorm<strong>en</strong>te limpiado cuando acabe la llamada. Esto significa que si se pasa a d-<br />

escribe()un objeto de una clase heredada de Pet, el compilador lo acepta, pero<br />

copia únicam<strong>en</strong>te el fragm<strong>en</strong>to del objeto que corresponda a una Pet. Se deshecha<br />

el fragm<strong>en</strong>to derivado del objeto:<br />

Figura 15.5: Object slicing<br />

Ahora queda la cuestión de la llamada a la función virtual. Dog::description()<br />

hace uso de trozos de Pet (que todavía existe) y de Dog, ¡el cual no existe<br />

porque fue truncado!. Entonces, ¿Qué ocurre cuando se llama a la función virtual<br />

El desastre es evitado porque el objeto es pasado por valor. Debido a esto, el compilador<br />

conoce el tipo exacto del objeto porque el objeto derivado ha sido forzado a<br />

transformarse <strong>en</strong> un objeto de la clase base. Cuando se pasa por valor, se usa el constructor<br />

de copia del objeto Pet, que se <strong>en</strong>carga de inicializar el VPTR a la VTABLE<br />

de Pet y copia sólo las partes del objeto que correspondan a Pet. En el ejemplo no<br />

hay un constructor de copia explícito por lo que el compilador g<strong>en</strong>era uno. Quitando<br />

interpretaciones, el objeto se convierte realm<strong>en</strong>te <strong>en</strong> una Pet durante el truncado.<br />

El Object Slicing quita parte del objeto exist<strong>en</strong>te y se copia <strong>en</strong> un nuevo objeto,<br />

<strong>en</strong> vez de simplem<strong>en</strong>te cambiar el significado de una dirección cuando se usa un<br />

puntero o una refer<strong>en</strong>cia. Debido a esto, el upcasting a un objeto no se usa a m<strong>en</strong>udo;<br />

de hecho, normalm<strong>en</strong>te, es algo a controlar y prev<strong>en</strong>ir. Hay que resaltar que <strong>en</strong> este<br />

ejemplo, si description() fuera una función virtual pura <strong>en</strong> la clase base (lo cual<br />

es bastante razonable debido a que realm<strong>en</strong>te no hace nada <strong>en</strong> la clase base), <strong>en</strong>tonces<br />

el compilador impedirá el object slicing debido a que no se puede "crear" un objeto<br />

de la clase base (que al fin y al cabo es lo que sucede cuando se hace un upcast<br />

por valor). ésto podría ser el valor más importante de las funciones virtuales puras:<br />

prev<strong>en</strong>ir el object slicing g<strong>en</strong>erando un error <strong>en</strong> tiempo de compilación si algui<strong>en</strong> lo<br />

456<br />

✐<br />

✐<br />

✐<br />

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

Saved successfully!

Ooh no, something went wrong!