11.6. Ejercicios

Las soluciones a los ejercicios se pueden encontrar en el documento electrónico titulado «The Thinking in C++ Annotated Solution Guide», disponible por poco dinero en www.BruceEckel.com.

  1. Convierta el fragmento de código «bird & rock» del principio de este capítulo a un programa C (utilizando estructuras para los tipos de datos), y que compile. Ahora intente compilarlo con un compilador de C++ y vea qué ocurre.

  2. Coja los fragmentos de código al principio de la sección titulada «Referencias en C++» y póngalos en un main(). Añada sentencias para imprimir en la salida para que pueda demostrar usted mismo que las referencias son como punteros que se dereferencian automáticamente.

  3. Escriba un programa en el cual intente (1) Crear una referencia que no esté inicializada cuando se crea. (2) Cambiar una referencia para que se refiera a otro objeto después de que se haya inicializado. (3) Crear una referencia nula.

  4. Escriba una función que tome un puntero por argumento, modifique el contenido de lo que el apunta puntero, y retorne ese mismo contenido como si de una referencia se tratara.

  5. Cree una nueva clase con algunos métodos, y haga que el objeto sea apuntado por el argumento del Ejercicio 4. Haga que el puntero pasado como argumento y algunos métodos sean constantes y pruebe que sólo puede llamar a los métodos constantes dentro de su función. Haga que el argumento de su función sea una referencia en vez de un puntero.

  6. Coja los fragmentos de código al principio de la sección «referencias a puntero» y conviértalos en un programa.

  7. Cree una función que tome como argumento una referencia a un puntero que apunta a otro puntero y modifique ese argumento. En main(), llame a la función.

  8. Cree una función que toma un argumento del tipo char& y lo modifica. En el main() imprima a la salida una variable char, llame a su función con esa variable e imprima la variable de nuevo para demostrar que ha sido cambiada. ¿Cómo afecta esto a la legibilidad del programa?

  9. Escriba una clase que tiene un método constante y otra que no lo tiene. Escriba tres funciones que toman un objeto de esa clase como argumento; la primera lo toma por valor, la segunda lo toma por referencia y la tercera lo toma mediante una referencia constante. Dentro de las funciones, intente llamar a las dos funciones de su clase y explique los resultados.

  10. (Algo difícil) Escriba una función simple que toma un entero como argumento, incrementa el valor, y lo retorna. En main(), llame a su función. Intente que el compilador genere el código ensamblador e intente entender cómo los argumentos se pasan y se retornan, y cómo las variables locales se colocan en la pila.

  11. Escriba una función que tome como argumentos un char, int, float y double. Genere el código ensamblador con su compilador y busque las instrucciones que apilan los argumentos en la pila antes de efectuar la llamada a función.

  12. Escriba una función que devuelva un double. Genere el código ensamblador y explique cómo se retorna el valor.

  13. Genere el código ensamblador de PassingBigStructures.cpp. Recorra y desmitifique la manera en que su compilador genera el código para pasar y devolver estructuras grandes.

  14. Escriba una simple función recursiva que disminuya su argumento y retorne cero si el argumento llega a cero, o en otro caso que se llame a sí misma. Genere el código ensamblador para esta función y explique la forma en el compilador implementa la recurrencia.

  15. Escriba código para demostrar que el compilador genera un constructor de copia automáticamente en caso de que no lo haga el programador. Demuestre que el constructor de copia generado por el compilador realiza una copia bit a bit de tipos primitivos y llama a los constructores de copia de los tipos definidos por el usuario.

  16. Escriba una clase en la que el constructor de copia se anuncia a sí mismo a través de cout. Ahora cree una función que pasa un objeto de su nueva clase por valor y otra más que crea un objeto local de su nueva clase y lo devuelve por valor. Llame a estas funciones para demostrar que el constructor de copia es, en efecto, llamado cuando se pasan y retornan objetos por valor.

  17. Cree un objeto que contenga un double*. Que el constructor inicialice el double* llamando a new double y asignando un valor. Entonces, que el destructor imprima el valor al que apunta, asigne ese valor a -1, llame a delete para liberar la memoria y ponga el puntero a cero. Ahora cree una función que tome un objeto de su clase por valor, y llame a esta función desde main(). ¿Qué ocurre? Solucione el problema escribiendo un constructor de copia.

  18. Cree una clase con un constructor que parezca un constructor de copia, pero que tenga un argumento adicional con un valor por defecto. Muestre que a pesar de eso se utiliza como constructor de copia.

  19. Cree una clase con un constructor de copia que se anuncie a sí mismo (es decir que imprima por la salida que ha sido llamado). Haga una segunda clase que contenga un objeto miembro de la primera clase, pero no cree un constructor de copia. Demuestre que el constructor de copia, que genera automáticamente el compilador en la segunda clase, llama al constructor de copia de la primera.

  20. Cree una clase muy simple, y una función que devuelva un objeto de esa clase por valor. Cree una segunda función que tome una referencia de un objeto de su clase. Llame a la segunda función pasándole como argumento una llamada a la primera función, y demuestre que la segunda función debe utilizar una referencia constante como argumento.

  21. Cree una clase simple sin constructor de copia, y una función simple que tome un objeto de esa clase por valor. Ahora cambie su clase añadiéndole una declaración (sólo declare, no defina) privada de un constructor de copia. Explique lo que ocurre cuando compila la función.

  22. Este ejercicio crea una alternativa a la utilización del constructor de copia. Cree una clase X y declare (pero no defina) un constructor de copia privado. Haga una función clone() pública como un método constante que devuelve una copia del objeto creado utilizando new. Ahora escriba una función que tome como argumento un const X& y clone una copia local que puede modificarse. El inconveniente de esto es que es el programador el responsable de destruir explícitamente el objeto clonado (utilizando delete) cuando haya terminado con él.

  23. Explique qué está mal en Mem.cpp y MemTest.cpp del Capítulo 7. Solucione el problema.

  24. Cree una clase que contenga un double y una función print() que imprima el double. Cree punteros a miembro tanto para el atributo como al método de su clase. Cree un objeto de su clase y un puntero a ese objeto, y manipule ambos elementos de la clase a través de los punteros a miembro, utilizando tanto el objeto como el puntero al objeto.

  25. Cree una clase que contenga un array de enteros. ¿Puede recorrer el array mediante un puntero a miembro?

  26. Modifique PmemFunDefinition.cpp añadiendo un método f() sobrecargado (puede determinar la lista de argumentos que cause la sobrecarga). Ahora cree un segundo puntero a miembro, asígnelo a la versión sobrecargada de f(), y llame al método a través del puntero. ¿Cómo sucede la resolución de la función sobrecargada en este caso?

  27. Empiece con la función FunctionTable.cpp del Capítulo 3. Cree una clase que contenga un vector de punteros a funciones, con métodos add() y remove() para añadir y quitar punteros a función. Añada una función denominada run() que recorra el vector y llame a todas la funciones.

  28. Modifique el Ejercicio 27 para que funcione con punteros a métodos.