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