You are on page 1of 18

C++ Estndar

Programacin con el Estndar ISO y la Biblioteca de Plantillas (STL) Paraninfo Thomson Learning 2001

Ejercicios resueltos por captulos.


Aqu encontrar los ejercicios resueltos por captulos (Tambin puede descargarse un fichero comprimido con todos los fuentes de los ejercicios)

PARTE I 1. Introduccin
No tiene ejercicios.

2. Conceptos bsicos
2.1) Quedara del siguiente modo: , hay otros tipos de comentarios como los de C++: empezar un comentario tipo C y ahora lo acabo. */ / * Que comentario ms precioso * / / / Este es ms precioso todava. 2.2) Si en un programa no ponemos la funcin funciones

main(), el programa no se podra enlazar. Si ponemos dos

main() ocurre lo mismo, el compilador da un error de redefinicin de una funcin.

2.3) El programa puede parecer a primera vista muy sencillo. En primer lugar vamos a leer y escribir una cadena. La primera solucin intuitiva: #include <iostream> // USAMOS: cin, cout void main() { char s[20]; // Cadena de hasta 19 caracteres cin >> s; // Leer la primera palabra cout << endl << s // Escribir en nueva lnea la cadena << endl; // Y pasar a la lnea siguiente } El problema es que esto nicamente nos lee la primera palabra de una cadena (esto se explicar en el captulo de entrada/salida). Aunque no se comprenda de momento, la solucin se encuentra en el fichero EJ02_03.CPP 2.4) Aqu est el programa: #include <iostream> // USAMOS: cin, cout

void main() { double d1, d2; out << "Introduce dos reales: "; cin >> d1 >> d2; cout << "La suma es: " << d1 + d2 << endl } 2.5) El programa correcto es ste: #include <iostream.h> // USAMOS: cout void main() { cout << "Hola mundo"; } 2.6) S es correcta. 2.7) Este comentario es errneo. Hemos dicho que los comentarios no se detectan en las cadenas. Pues no es completamente cierto. No se detecta su apertura pero s su clausura. Por ello, las sentencias se convertiran en: "; */ La solucin sera definir de nuevo las sentencias como: /* cout << "/* Me pillaste *""/"; // Concatenacin de cadenas */

3. Tipos de datos
3.1) La funcin es: int Convierte(char c) { return int(c - '0'); } // Usando int() en vez de (int) se ahorra un par de parntesis 3.2) S que es vlido ya que en C++ todos los tipos integrales son compatibles. Aunque sera mucho mejor explicitar las conversiones: b= (byte)w; w= (word)l; d= (dword)w; 3.3) Su longitud es 9 tomando

sizeof(int) == 2.

3.4) La primera dar error ya que 8 no es un dgito octal y por tanto 08 es un error. La segunda dar 24 porque 014 est en octal que es 12 en decimal.

4. Control de Flujo
4.1) El listado es funcionalmente correcto pero sintcticamente no. Faltan los puntos y comas de las cuatro sentencias de asignacin. if (a < b) if (a < c)

min=
else min= c; else if (b > c) min= c; else min= b;

a;

4.2) Programa que cuenta el nmero de ocurrencias en una cadena de las 5 vocales en 5 variables diferentes: a, e, i, o, u. Usaremos la funcin Leer_Cadena() del ejercicio 2.3. El programa est en EJ04_02.CPP 4.3) Una funcin que calcule el M.C.D. de dos nmeros: int Mcd(int a, int b) { if (a <= 0 || b <= 0) return -1; // Cdigo de error while (a != b) if (a < b) b= b - a; // b-= a; // Igual que la anterior. Ya se ver else a= a - b; // a-= b; // Igual que la anterior return a; // Al final el mcd est en a y en b (a == b) } Un ejemplo de uso de la funcin est en EJ04_03.CPP 4.4) Funcin que compara dos cadenas: int StrCmp(char *s1, char *s2) { int i= 0; while (s1[i] || s2[i]) { // Hasta terminar las dos if (s1[i] < s2[i]) return -1; // La cadena 1 es menor que la 2 else if (s1[i] > s2[i]) return 1; // La cadena 1 es mayor que la 2 i++; } return 0; // Las cadenas son iguales } Esta funcin es similar a strcmp() de la librera estndar <cstring> del C++. Un ejemplo de uso de la funcin est en EJ04_04.CPP 4.5) Slo la b) es correcta. Recordemos que la sentencia: for(a; b; c); se convierte en:

{ a; while(b) { d; c; } } Por tanto, las sentencias anteriores son: a) int i= 0, int j= 0; // Incorrecto // Dos declaraciones separadas por una coma while .. b) int i= 0, j= 0; // Correcto // Dos declaraciones de tipo entero while .. c) int i= 0, long j= 0; // Incorrecto while .. d) (int i = 0), (long j = 0) // Incorrecto // Lstima porque era una buena idea 4.6) La solucin se encuentra en el fichero EJ04_06.CPP 4.7) En este ejercicio se ha pretendido aumentar la atencin del lector en este error comn y sutil pero difcil de detectar. La condicin del bucle est formada por el operador de asignacin (=) y no el operador de comparacin (==), con lo que el resultado del programa es que slo muestra un 0, ya que el resultado de la asignacin i=10, adems de asignar 10 a la variable i, es que devuelve el valor 10, que es un valor cierto, al ser no nulo. Si despus lo negamos con el operador ! obtenemos falso, con lo que el bucle sale despus de la primera iteracin. 4.8) La solucin se encuentra en el fichero EJ04_08.CPP 4.9) La solucin se encuentra en el fichero EJ04_09.CPP 4.10) La solucin se encuentra en el fichero EJ04_10.CPP 4.11) La solucin se encuentra en el fichero EJ04_11.CPP

5. Operadores
5.1) Es simple: x & (x - 1) 5.3) Se supone que tenemos dos valores enteros almacenados en dos variables reales. Yo lo hara as: float Resto(float a, float b) { return float((long)a % (long)b); } 5.4) NO ES VLIDO PORQUE EL OPERADOR COMA NO SE PUEDE UTILIZAR EN ESE PARTE DEL FOR. Si cogemos uno de los dos incementos y lo ponemos al final del bucle s que funciona. En este caso invierte el vector de caracteres s (no es una cadena porque no acaba en '\0'). El resultado en s ser ACABATOR. 5.5) Con algo parecido a esto sera suficiente para que pareciera aleatorio. Si adems hacemos coincidir la llamada a Rand() con un factor externo (tiempo, preferiblemente), esta funcin es casi impredecible. El programa se encuentra en EJ05_05.CPP

6. Funciones
6.1) La solucin se encuentra en el fichero EJ06_01.CPP 6.2) La solucin se encuentra en el fichero EJ06_02.CPP 6.3) Es sintcticamente correcto. El compilador crea variables temporales para almacenar estas constantes y as ya puede tomar la direccin. De todas formas no es un buen estilo de programacin pasar constantes por referencia porque aunque la funcin modifique su valor no nos podemos dar cuenta.

f(25) es ambigua. El compilador no sabe si llamar a la funcin con un argumento o llamar a la segunda usando parmetros por defecto. La llamada f(17, 42)es completamente correcta ya que no hay
6.4) La llamada ambigedad. 6.5) S que es correcto y sera equivalente a: void f(int a= 1, int b= 2, int c= 3) { // .. } 6.6) La solucin se encuentra en el fichero EJ06_06.CPP

7. Variables
7.1) Las variables estticas se inicializan a 0. Las variables automticas no. Por tanto a valdr 0 y b tendr un valor indefinido dependiendo del compilador. No se recomienda usar la declaracin de 'a' de ese modo. Es mejor explicitar: int a= 0; 7.2) Este sera un programa que volvera loco al propio Bjarne Stroustrup: void Funcion(float f); // Decl1. Campo prototipo float f; // Decl 2. Campo global. Se almacena en el seg. de datos. f vale 0 void Funcion(float f) { // Decl. 2. Campo local automtico. Se almacena en pila float f; // Error: parmetros y var. locales tienen el mismo campo auto float a; // Este auto es opcional // Decl.3.Campo local automtico. Se almacena en pila. a vale ? static float f; // Error: mismo campo. static float s; // Decl.4.Campo local esttico. Se almac. en el s. de datos. s vale 0 { float f; // Decl. 5. Campo de bloque. Se almacena en la pila f= 2; // Accedo a la 'f' de la decl. 5 ::f= 3; // Accedo a la 'f' de la decl. 1

s= 4; // Accedo a la 's' de la decl. 4 a= 5.5; // Accedo a la 'a' de la decl. 3 // No hay forma de acceder al parmetro 'f' de la funcin (Decl. 2) } } float f; // Error! Redefinimos la variable global. 7.3) Como hemos visto en el caso anterior, no es correcto ya que los dos tienen el mismo campo local automtico. 7.4) Dar un error en la definicin const int de la definicin. Las otras dos tambin daran error. 7.5) No sera equivalente a: const char * Var; sino a: char * const Var; porque

a ya que las constantes se deben inicializar en el momento

typedef no es una macro.

7.6) El programa A funciona correctamente. El programa B da error porque no sabemos cmo es la estructura, por tanto, no podemos definir una variable de ella. El programa C funcionara si no se tratara de una estructura. Ya se vio que extern slo es aplicable a variables de tipos no compuestos. 7.7) Este sera un programa que volvera loco al propio Bjarne Stroustrup: void Funcion(float f); // Decl1. Campo prototipo float f; // Decl 2. Campo global. Se almacena en el seg. de datos. f vale 0 void Funcion(float f) { // Decl. 2. Campo local automtico. Se almacena en pila float f; // Error: parmetros y var. locales tienen el mismo campo auto float a; // Este auto es opcional // Decl.3.Campo local automtico. Se almacena en pila. a vale ? static float f; // Error: mismo campo. static float s; // Decl.4.Campo local esttico.Se almac. en el s. de datos. s vale 0 { float f; // Decl. 5. Campo de bloque. Se almacena en la pila f= 2; // Accedo a la 'f' de la decl. 5 ::f= 3; // Accedo a la 'f' de la decl. 1 s= 4; // Accedo a la 's' de la decl. 4 a= 5.5; // Accedo a la 'a' de la decl. 3

// No hay forma de acceder al parmetro 'f' de la funcin (Decl. 2) } } float f; // Error! Redefinimos la variable global.

8. Sobrecarga y conversiones
8.1) En C++, las constantes tienen tipo por lo que el compilador asignar: - la primera es un

int . Coincidencia exacta con Print(int ).

- la segunda es un double. No hay coincidencia exacta ni trivial. No hay promocin. Hay conversin estndar. Pero las conversiones estndar de un tipo aritmtico puede ser a cualquier otro tipo aritmtico. Por tanto, fallar porque hay una ambigedad. Podramos haberlo solventado poniendo 2.2F. - la tercera es un a

char. No hay coincidencia exacta ni trivial. Pero hay promocin con int; por tanto, se llama Print(int ).

En general, las posibles soluciones a los problemas que aparecen (como el de la segunda llamada) son: a) Declarar variables auxiliares del tipo que se desee. b) Forzar que las constantes sean del tipo requerido. c) Utilizar conversiones explcitas (cast) 8.2) S, no hay coincidencia exacta o trivial, no hay promociones, pero hay conversin estndar aritmtica. Por tanto, se llama sin ningn problema. 8.3) No porque tomar

f() como float y no como una funcin. Concretamente, dar un error de llamada a no-funcin ("call of non-function") ya que estamos intentando llamar a un float como si fuera una funcin.
8.4) La solucin se encuentra en el fichero EJ08_04.CPP

8.5) La solucin se encuentra en el fichero EJ08_05.CPP 8.6) Dara error al haber conversin trivial entre

const T y T.

8.7) Como no hay conversin trivial, se podra definir perfectamente. 8.8) Las dos primeras llamadas invocan a sus funciones correspondientes sin ningn problema. La tercera sigue estos pasos: Primero: no hay coincidencia exacta. Segundo: no hay promocin. Tercero: conversin estndar, pero la hay a los dos, no le damos preferencia a la que no tiene signo. Por tanto dara error de ambigedad. 8.9) La solucin se encuentra en el fichero EJ08_09.CPP 8.10) La solucin se encuentra en el fichero EJ08_10.CPP 8.11) Son correctas. Se trata de conversiones de lnea. 8.12) En C++,

typedef no crea tipos nuevos distintos, slo les da un nombre diferente.


1.- El literal f(double ). 2.- El literal

8.13) Para las cinco llamadas, el proceso es bien diferente:

0.0 es un double. Pasos: Primero: coincidencia exacta. Por tanto se llama a 0 es un int. Pasos: Primero: no hay coincidencia exacta. Segundo: no hay int a char y double. Adems, dijimos que la constante 0 tiene conversin estndar con

promocin posible. Tercero: hay conversin estndar de

deint a cualquier puntero. Por tanto habr error de ambigedad al no poder elegir ninguna de las tres funciones.

3.- El literal

0F da error de sintaxis, ya que F slo se puede aplicar a constantes reales.

4.- El literal 0.0F es un float. Pasos: Primero: no hay coincidencia exacta. Segundo: hay promocin de float a double. Por tanto se llama af(double ).

char *. Pasos: Primero: no hay coincidencia exacta. Segundo: no hay promocin. Tercero: hay conversin estndar entre char * yvoid *. Por tanto se llama a f(void *).
5.- El literal cadena es un 8.14) Para la primera combinacin, la segunda llamda es correcta (mismo tipo), pero la primera no, porque no hay conversin estndar desde int a enum. Como si est permitido lo contrario, la combinacin dos es perfectamente correcta. La combinacin tercera tambin lo es, llamando cada una a su correspondiente funcin. 8.15) El compilador da un error de ambigedad, ya que no sabe si llamar a o ff(fc) con signo. Qu complicados son los complicadores!

ff(fc) sin signo

9. Punteros
9.1) El primero carga en variable

p la direccin de la variable a (p= &a), pero al cerrarse el bloque la a se destruye con lo que el acceso posterior de (*p= 10) puede ser catastrfico.

El segundo programa, en cambio, funciona correctamente ya que el carcter a tratar se almacena en el 'heap' y no en la pila, as al cerrar el bloque no destruimos ninguna variable ya que no hemos definido ninguna tampoco. El acceso (*p= 10) ser vlido hasta que pongamos (delete p;). Una mejor solucin sera: void main() { char *p; int a; { p= &a; } *p= 10; } 9.2) Invierte una cadena. La solucin se encuentra en el fichero EJ09_02.CPP 9.3) La solucin se encuentra en el fichero EJ09_03.CPP 9.4) La solucin se encuentra en el fichero EJ09_04.CPP 9.5) La solucin se encuentra en el fichero EJ09_05.CPP 9.6) Ese programa es muy peligroso. Leemos una cadena en s, pero s apunta a una direccin indefinida; por ello, podemos estar estropeando cdigo, datos de nuestro o de otro programa. Adems no se puede asegurar que la salida sea igual que la entrada. En fin, que este es uno de los errores ms graves y tpicos del C++. Adems, puede que en un primer momento funcione. Ms tarde el error aparecer inesperadamente de forma catastrfica. La solucin es reservar la memoria que vamos a usar: #include <iostream.h> void main() { char s[100]; // Suponemos que con 100 caracteres es suficiente

cin >> s; cout << s; } Tambin podramos haber usado: #include <iostream.h> void main() { char *s; s= new int[100]; cin >> s; cout << s; delete []s; } 9.7) No ocurre nada, al final del programa el compilador se encarga de hacer todos los delete que falten. De todas formas, es muy recomendable no olvidarse de ponerlo porque si es en una funcin que se llama 1000 veces acabaremos con el 'heap' lleno!. Tampoco es muy recomendable hacer lo que se ha hecho en el ejercicio 1, pero a veces como en ese ejercicio, es necesario. 9.8) Para hacer lo que se nos pide en el ejercicio habra que hacer uso de punteros: float f; int *pi= (int *)&f; char *pc= (char *)&f; Y con f, *pi, *pc accederamos a lo mismo que con la unin: f, i, c. Claramente, usar una unin annima es ms limpio aunque con punteros se ve fsicamente que comparten la misma memoria. En este caso, trabajar con punteros puede ser peligroso, ya que si tenemos: char c; int *pi= (int *)&c; float *pf= (float *)&c; un acceso a (*pi) a (*pf) excedera del tamao del carcter, estropeando lo que hay despus en memoria, que en este caso es el puntero que accede. Aqu, se puede decir, que est casi asegurado que el sistema se quede bloqueado o lo que en el argot se conoce como "colgado". 9.9) El programa compara los punteros, no donde apuntan. Si lo sustituyramos por(*s == *t) tampoco ya que slo comparara el primer elemento. Queda como ejercicio hacer una funcin que compare cadenas. En el siguiente captulo tambin se vern algunas funciones de comparacin. 9.10) No es correcto porque hemos definido p como un puntero a enteros constantes sobre los cuales nos podemos hacer un delete. Adems, delete p slo borrara el primer elemento, en el caso de que no fuera

const.

9.11) Los dos son, obviamente, equivalentes y ninguno de ellos da error. El puntero retornado en p es indefinido y la direccin a la que apunte no est reservada. No retorna NULLcomo podramos imaginar en un principio, del mismo modo que

delete no modifica el puntero, sino simplemente libera la memoria.

9.12) Intentar borrar slo una parte del vector reservado es una barbaridad, no porque sea ilgico pensarlo, sino porque el C++ no lo detecta como error y dependiendo de la implementacin, puede ser que no ocurra nada o se convierta en un desastre. Lo nico que sabemos con seguridad es que si hacemos lo correcto, no tendremos ningn problema.

10. Eficiencia y Optimizacin


10.1) La solucin se encuentra en el fichero EJ10_01.CPP 10.2) La solucin se encuentra en el fichero EJ10_02.CPP 10.3) Tenemos un tipo

reloj y tres funciones:

reloj Start_Timer(); // Crea un reloj y lo pone en marcha double Get_Timer(reloj &); // Retorna el tiempo en segundos desde que se creo este reloj double Stop_Timer(reloj &); // Igual que Get_Timer() pero adems destruye el reloj Adems se ha implementado una funcin de retardo calibrar durante unos segundos la funcin milisegundos como se le pasen en su parmetro. Adems tenemos una funcin

Delay(unsigned long ) que tarda tantos Calibrar(int ) para Delay(). El listado de la implementacin es EJ10_03.CPP

10.4) Usando las funciones necesarias del ejercicio anterior veamos EJ10_04.CPP. En muchas mquinas saldr Fact3() la ms rpida y Fact2() la ms lenta, completamente al contrario de lo que podramos pensar en un principio. Esto depende de cmo estn orientados los procesadores, si tienen antememorias (cachs), si son mquinas RISC o CISC, etc. 10.5) Se prueba en el siguiente ejercicio. 10.6) La solucin se encuentra en el fichero EJ10_06.CPP 10.7) La funcin al ser y eficiencia a

inline hara que cualquier aparicin de alias(i) fuera equivalente en sentido

i.

10.8) La solucin se encuentra en el fichero EJ10_08.CPP 10.9) Una forma de implementar el algoritmo quicksort() est en EJ10_09.CPP 10.10) La multiplicacin por potencias de dos se puede realizar por medio de desplazamientos de bit. Ejemplos para 2, 4 y 8 seran: inline int Mult2(int a) { return a << 1; } inline int Mult4(int a) { return a << 2; } inline int Mult8(int a) { return a << 3 } que por las pruebas que se han realizado son ligeramente ms rpidas que la multiplicacin normal. Esto depende mucho de la mquina. Las funciones para la divisiones son similares pero utilizando el operador >>.

10.11) Se tratara de lo siguiente: inline int Mult3(int a) { return Mult2(a) + a; } inline int Mult5(int a) { return Mult4(a) + a; } inline int Mult6(int a) { return Mult4(a) + Mult2(a); } inline int Mult7(int a) { return Mult(6) + a; } inline int Mult9(int a) { return Mult8(a) + a; } Segn vamos aumentando iremos perdiendo en eficiencia. La reutilizacin de unas funciones en otras no ralentiza ya que son inline. En general: int Mult(int a, int b) { int r= 0; while (b) { if ((b | 1) == b) // bit es 1 r+= a; b >>= 1; a <<= 1; } return r; } que ya no es eficiente. Esta solucin y medidas de tiempo se encuentran en el fichero EJ10_11.CPP 10.12) Es correcto ya que en los macros los comentarios no son expandidos. Esto se ver mejor cuando se vea preprocesamiento. 10.13) El algoritmo se encuentra en EJ10_13.CPP. 10.14) La solucin se encuentra en el fichero EJ10_14.CPP

PARTE II 11. Clases


11.1) Al hacer siguiente gente que empieza puede quedar ms claro poniendo: delete this; this= this->Ultimo; que es lo mismo que antes pero ahora se ve que el puntero this al que hemos hecho un delete, lo utilizamos como fuente en la siguiente sentencia. Por ello, se suele utilizar el puntero this para acceder a los atributos de una clase cuando queda comprometida la claridad. En segundo lugar como this es un puntero constante ni se puede hacer un delete sobre l ni se puede poner como destino en una asignacin. 11.2) El programa es completamente correcto. El primer objeto parmetro 3. Por tanto

delete this estamos liberando la memoria que ocupa el objeto actual por lo que el this= Ultimo ya no es vlido porque el Ultimo puede haber perdido su valor. Para la

Obj1 llama al Pon de la clase c1 con el Obj1.Valor se pone a 3. El segundo objetoObj2 llama al Pon de la c1 y

clase c2 sin parmetros por lo que se toma el parmetro 1 por defecto que es lo que se almacena en Obj2.Valor. Al hacer Obj3.Valor= 10 no modificamos ningn otro objeto ni de mucho menos de 11.3) Las dos son

c2 que no tiene nada que ver. inline.

11.5) La solucin se encuentra en el fichero EJ11_05.CPP 11.6) La solucin se encuentra en el fichero EJ11_06.CPP 11.7) La solucin se encuentra en el fichero EJ11_07.CPP 11.9) En primer lugar no funcionara porque hemos definido los mtodos privados. Solventando este problema no funcionara tampoco porque cuando se llama a una funcin inline debe tener su implementacin ya definida. En este caso la solucin sera cambiar de orden f1() y f2(). class clase { .. public: void f1(); void f2(); .. }; inline void clase::f2() { .. } void clase::f1() { .. f2(); .. }

void main() { clase o; o.f1(); } Una curiosa solucin es poner las dos funciones inline, as las funciones no son evaluadas hasta que se expanden, que en este caso ocurrir cuando lleguemos a main(), pasadas ya las definiciones de

f1() y f2(). Otra solucin, evidentemente, es no definir ninguna inline.

11.10) La solucin se encuentra en el fichero EJ11_10.CPP 11.11) Si hacemos la implementacin de los complejos en forma polar, no quita para que definamos exactamente los mismos mtodos, incluso los constructores. Por tanto, si slo visemos las declaraciones de los mtodos, no podemos saber si estn implementados en forma rectangular o en forma polar. 11.12) La primera sentencia modifica el parmetro c1. La segunda modifica el miembro c2. La tercera modifica la variable global c3. La cuarta sentencia, modifica el miembro c1 al usar this. La quinta sentencia tambin al utilizar el operador de campo de clase. 11.13) No se puede. Deberemos hacer: class clase { static int Estatuto; .. }; int clase::Estatuto= 23; o definir un mtodo esttico para acceder a int clase::Estatuto; se debe seguir poniendo. 11.14) Perfectamente. Aunque no tiene mucho sentido. 11.15) No podemos acceder a a porque f() es una funcin esttica y por tanto no tenemos el parmetro implcito this para poder acceder a los miembros. 11.16) La solucin se encuentra en el fichero EJ11_16.CPP

Estatuto. De todas formas la sentencia:

12. Creacin de objetos


12.1) Los mtodos son: a) constructor normal b) constructor por defecto c) exactamente igual a lo anterior d) constructor copia e) constructor por defecto (todos los argumentos por defecto) y constructor de conversin de (int *) a c1 f) constructor de conversin de float a cl si se toma el ltimo argumento por defecto, si no, constructor normal g) operador suma h) operador de conversin de cl a int. No se pone retorno i) operador de asignacin

j) destructor k) Error! Los destructores no tienen parmetros 12.2) No es correcta ya que funcin

punto(c.RE(), c.IM()) crea un objeto temporal en el cuerpo de la punto(complejo c) y no modifica 'x' e 'y'. La solucin sera:

class punto { private: double x, y; public: void Pon(double xx, double yy) { x= xx; y= yy; } punto(double xx, double yy) { Pon(xx, yy); } punto(const complejo & c) { Pon(c.RE(), c.IM()); } }; 12.3) Tiene dos problemas, el primero es que como no hemos puesto ningn modificador de acceso y se trata de 'class', el mtodo f() ser privado con lo que no lo podremos llamar. En segundo lugar, no podemos llamar a funciones no constantes desde objetos constantes. En este caso, esta ltima restriccin es una garanta de seguridad de que el objeto si es constante no va a ser modificado. 12.4) Depender del orden en que estn definidos dentro de una clase. Lo nico que sabemos con seguridad es que x pasar a valer a antes de que y pase a valer b. Esto es debido a que los dos se construyen en la lista de inicializacin, como que esperar a valer

'x' si que est en la lista se construye y toma su valor a la vez mientras que 'y' tiene 'b' al cuerpo de la funcin.

12.5) Para la primera no, ya que al haber otro constructor, ya no est definido el constructor por defecto. En la segunda s ser posible (en la versin 2.0 no). Las dos ltimas son perfectamente vlidas. 12.6) Se crear un objeto temporal por lo que el objeto constante no puede ser modificado por mucha referencia de que se trate. 12.8) Ver EJ12_08.CPP 12.10) S se pueden definir miembros (atributos y mtodos) 12.11) El operador

volatile.

sizeof no puede sobrecargarse.

12.12) En primer lugar, falta el punto y coma final de la clase. En segundo lugar, nunca deberemos hacer una asignacin a this y mucho menos un delete. 12.13) Es correcto aunque es ms recomendable definir la unin dentro de una clase. 12.14) Genera ambigedad. No sabemos si llamar a f(A(Objeto)); o a:

f(Objeto.operator A ());

12.15) No funcionara porque en C++ no se buscan conversiones multinivel. Y no hay ninguna conversin en un solo paso para hacer coincidir los parmetros. 12.16) Al llamarse a la funcin Nada() que parece que no hace nada, se crea un objeto temporal usando el constructor copia. El constructor copia que tenemos definido slo copia los atributos. Al llegar al final del cuerpo de la funcin (en seguida porque no hace nada), se retornara llamando al destructor del objeto temporal, que de la forma que tenemos definida la cadena hara un delete s; liberando la memoria. Cuando hiciramos cout << c1; probablemente salga la cadena por pantalla, pero no se puede asegurar, ya que hemos liberado la memoria que ocupa y puede ser utilizada por cualquier otro. Lo peor no es esto, sino que al llegar al final de la funcin se destruira c1 volviendo a llamar a delete sque ya est borrado. Esto lo suele avisar el compilador por medio de un error al final del programa del tipo "Null pointer assignment" 12.17) La solucin se encuentra en el fichero EJ12_17.CPP

13. Herencia y Polimorfismo


13.1) La asignacin e) es incorrecta ya que no podemos asignar un clase derivada con una clase base. La asignacin f) nos muestra que esto es imposible incluso utilizando casts. La asignacin h) es incorrecta por el mismo motivo que la e). Pero la i) es correcta porque siempre podemos pasar de un puntero de un tipo a un puntero de otro tipo utilizando casts. 13.2) No, no tiene sentido heredar dos veces ya se virtual o no virtual. Si se quiere incluir dos veces una clase se hace precisamente eso, incluir (composicin). a

c1 que es un puntero cuadrilatero. La segunda sentencia es incorrecta ya que no se puede asignar un puntero a un cuadrilatero a un puntero a un cuadrado. En el segundo lugar podramos usar un cast pero si los mtodos no
13.3) La primera crea un objeto dinmico de la clase cuadrado y toma su direccin en cuadrado *c2= (cuadrado *)new cuadrilatero;

son virtuales puede ser peligroso. 13.4) La longitud es 4 + 4 + 4 = 12 suponiendo 4 la longitud de int, 4 la longitud de float y 4 la longitud del puntero a la tabla de mtodos virtuales (suponiendo punteros de 32 bits). 13.5) Las funciones

f() darn error ya que tienen los mismos parmetros y distinto tipo de retorno. En cambio

las funciones g() son funciones totalmente diferentes ya que tienen parmetros distintos. Por tanto B heredar g(int, double) y tendr adems g(double, int). 13.6) Son los dos virtuales ya que si definimos un destructor como virtual en una clase base, los destructores en las clases heredadas tambin sern virtuales. En estos casos se recomienda poner la palabra virtual para dejarlo ms claro. Se deja como ejercicio averiguar si teniendo dos clases A y B, una con destructor virtual y la otra normal, si heredamos las dos en una clase C, el destructor de C ser virtual? 13.7) La solucin se encuentra en el fichero EJ13_07.CPP

14. Plantillas
14.1) S que podemos compilar ese programa, pero en el momento que usemos la funcin solucin es simplemente borrar la primera declaracin ya que la segunda la incluye.

f() dar error. La

14.2) Porque el tipo A no est incluido en los parmetros de la funcin. Ya sabemos que el retorno no cuenta. 14.3) Ver EJ14_03.CPP. Se han definido algunos mtodos internos y otros externos para mostrar el acceso a vector. Sera preferible todos externos. 14.4) Que no se puede hacer coincidir (C valdra

*) con (int). Si hubiera sido (C) no habra problema, C

int.

14.5) La solucin vale para cualquier tipo:

template <class T> int SizeOf_EnBits(T v) { return sizeof(T) * 8; //return sizeof v * 8; // Tambin vlido } 12.6) No, pero la solucin es simple: typedef clase1<int> clase1_int; clase2 <clase1_int> a;

15. Errores y Excepciones 16. Modularidad


16.1) Porque ya vienen provistas del mtodo de proteccin contra redefiniciones que hemos explicado. 16.2) Funcionara muy mal ya que no hemos definido el constructor copia y el operador de asignacin. Al tratarse de una estructura dinmica, cada vez que llamemos implcitamente al constructor copia (por ejemplo con objetos temporales), deberamos copiar toda la estructura y slo copiamos la direccin. Pero cuando destruimos los objetos temporales, s que destruimos toda la estructura. En resumidas cuentas, que vamos a destruir ms veces que a construir. 16.3) Aqu viene la solucin al ejercicio anterior. Se compone de tres ficheros EJ16_03.H, EJ16_03.H1, EJ16_03.H2. Adems tenemos un ficheroEJ16_03.CPP que nos lo prueba todo. El fichero EJ16_03.H como vemos, queda limpio de toda implementacin. En primer lugar incluye EJ16_03.H1 en la parte privada de la clase y despus incluye fuera a EJ16_03.H2. Como se observa lo nico que ve el usuario son los mtodos de la lista. No se puede saber si est implementada dinmica o estticamente, no se sabe nada de sus miembros privados, ni estticos, slo lo imprescindible que debe conocer el que usa esta clase. La parte privada de la clase (los atributos) est en el fichero EJ16_03.H1. Aqu se definen los miembros privados. Es de resaltar la presencia interna de como clase amiga de

nodo. Se podra haber definido

clista, pero como en este caso slo la utilizamos aqu, la incluimos dentro del campo de la funcin. Pasemos ahora a la implementacin de los mtodos. Estn en el fichero EJ16_03.H2.

El ltimo mtodo (Alias) se suele incluir para hacer referencias. Esto sirve para que tengamos varias listas operando sobre los mismos datos. Esto suele ser peligroso por los objetos temporales y porque la destruccin de uno implica que se ha liberado el espacio al que apuntan todos. Por eso no lo vamos a usar. Por ltimo, hay un fichero que lo prueba todo; este fichero slo debe incluir la especificacin. Es el fichero EJ16_03.CPP. COMENTARIOS: Los operadores de postincremento y postdecremento retornan por valor. As, operaciones como la siguiente, estaran permitidas pero no funcionaran de manera correcta: ++l1++; Slo incrementara una vez l1, el otro operador actuara sobre un objeto temporal retornado por el primero. En resumen, este artificio de estructura modular es un poco largo de realizar y difcil de entender para el que lo desarrolla. Pero nadie puede dudar que el fichero EJ16_03.H est claro como el agua. 16.5) S que compilara y enlazara. Al tener los dos el atributo const tienen acceso privado al mdulo, por lo que no son visibles externamente y no habra conflicto entre ellas. De todas formas, sera mucho ms conveniente, poner una sola declaracin en una cabecera e incluirla en los dos mdulos.

PARTE III 17. Introduccin a las Libreras Estndar


No tiene ejercicios.

18. Entrada y salida


18.1) La solucin se encuentra en el fichero EJ18_01.CPP 18.2) En cada caso saldra: a) 00103 b) 10 // Todava no haba hecho efecto c) a 18.3) El programa debe incluir algunos manipuladores y flags para que no se ignoren los caracteres blancos. #include <iostream.h> #include <iomanip.h> void main() { cin >> resetiosflags(ios::skipws); while (1) { char c; cin >> c; if (!cin) break; cout << c; } } Tambin se poda haber hecho as: while (1) { char c; cin.get(c); if (cin.eof()) break; // Fin copia cout.put(c); } que no utiliza manipuladores. 18.4) No ya que el 00069 18.5) No ya que ya est sobrecargado en

setw(100) acta sobre un stream (cin) y el setw(5) acta sobre otro

(cout). Adems, la salida sera:

<iostream.h> en las clases istream y ostream.

18.6) Tenemos aqu un programa que produce un volcado hexadecimal de un fichero de entrada a otro de salida. Si se omiten estos ficheros se cogern por defecto la entrada y salida por pantalla estndar. Ver EJ18_06.CPP 18.7) La solucin es:

int i= 42; const char *fn= "test.dat" const int Largo = 7; { fstream f(fn, ios::out | ios::binary); f.seekp(Largo, ios::beg); f.write((const char *)&i, 2); } // Al destruirse se cierra { fstream f(fn, ios::in | ios::binary); f.seekg(Largo, ios::beg); f.read((char *)&i, 2); } // Al destruirse se cierra Tambin podamos haber llamado a los destructores explcitamente. Ya que estamos con la programacin orientada a objetos, es ms lgico utilizar constructores y destructores. Principalmente, lo que no hay que hacer es mezclar los dos mtodos. 18.8) Simplemente hay que saber el cdigo del descriptor de la impresora (que suele ser 4). Utilizamos entonces el constructor ofstream(int fh): fstream Stream_de_la_impresora(4); Si queremos utilizar buffer: char *Buffer= new char [1024]; ofstream Str_de_la_impr(4, Buffer, 1024); 18.9) La funcin

Leer_Cadena() la definimos as:

#include <iomanip.h> // resetiosflags void Leer_Cadena(char *s) { cin >> resetiosflags(ios::skipws); // No pasar los caracteres blancos for(int i= 0; cin >> s[i]; i++) // Leer hasta '\0' if (s[i] == '\n') // Se ha pulsado INTRO break; s[i]= '\0'; // Poner caracter nulo de terminacin de cadena }