Sei sulla pagina 1di 16

C++ Estndar

Programacin con el Estndar ISO y la Biblioteca de Plantillas (STL)


Paraninfo Thomson Learning 2001
http://users.dsic.upv.es/~jorallo/libro_c++/fuentes/ejercicios.html
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 main(), el programa no se podra enlazar. Si ponemos dos funciones 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= a;
else
min= c;
else
if (b > c)
min= c;
else
min= b;
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.
6.4) La llamada 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 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 a ya que las constantes se deben inicializar en el momento de la definicin. Las otras dos
tambin daran error.
7.5) No sera equivalente a:
const char * Var;
sino a:
char * const Var;
porque 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 char. No hay coincidencia exacta ni trivial. Pero hay promocin con int; por tanto, se llama a 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.
8.13) Para las cinco llamadas, el proceso es bien diferente:
1.- El literal 0.0 es un double. Pasos: Primero: coincidencia exacta. Por tanto se llama a f(double ).
2.- El literal 0 es un int. Pasos: Primero: no hay coincidencia exacta. Segundo: no hay promocin posible. Tercero: hay conversin estndar
de int a char y de int a double. Adems, dijimos que la constante 0 tiene conversin estndar con 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 a
f(double ).
5.- El literal cadena es un char *. Pasos: Primero: no hay coincidencia exacta. Segundo: no hay promocin. Tercero: hay conversin
estndar entre char * y void *. Por tanto se llama a f(void *).

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 ff(fc) sin signo o ff(fc) con signo. Qu complicados son los
complicadores!

9. Punteros
9.1) El primero carga en p la direccin de la variable a (p= &a), pero al cerrarse el bloque la variable 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 NULL como 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 Delay(unsigned long ) que tarda tantos milisegundos como se le pasen en su
parmetro. Adems tenemos una funcin Calibrar(int ) para calibrar durante unos segundos la funcin 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 inline hara que cualquier aparicin de alias(i) fuera equivalente en sentido y eficiencia a 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 delete this estamos liberando la memoria que ocupa el objeto actual por lo que el siguiente this= Ultimo ya no es vlido
porque el Ultimo puede haber perdido su valor. Para la 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 Obj1 llama al Pon de la clase c1 con el parmetro 3. Por tanto Obj1.Valor se
pone a 3. El segundo objeto Obj2 llama al Pon de la 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 c1 y mucho menos de c2 que no tiene nada que
ver.
11.3) Las dos son 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 Estatuto. De todas formas la sentencia:
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
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 punto(c.RE(), c.IM()) crea un objeto temporal en el cuerpo de la funcin 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 'x' si que est en la lista se
construye y toma su valor a la vez mientras que 'y' tiene que esperar a valer '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) volatile.
12.11) El operador 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 s que 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).
13.3) La primera crea un objeto dinmico de la clase cuadrado y toma su direccin en c1 que es un puntero a 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 son virtuales puede ser peligroso.
cuadrado *c2= (cuadrado *)new cuadrilatero;
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 f() dar error. La solucin es simplemente borrar
la primera declaracin ya que la segunda la incluye.
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 *) con (int). Si hubiera sido (C) no habra problema, C valdra 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
fichero EJ16_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 nodo. Se podra haber definido como clase amiga de 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 setw(100) acta sobre un stream (cin) y el setw(5) acta sobre otro (cout). Adems, la salida sera:
00069
18.5) No ya que ya est sobrecargado en <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
}
En primer lugar hacemos que no se quiten los caracteres blancos. Luego leemos hasta encontrar uno de estos dos caracteres '\0', '\n'. ste
ltimo se producir cuando pulsemos la tecla de retorno. Al final deberemos poner el carcter nulo ya que el stream no lo inserta.
19. Cadenas y Numricos
19.1) La solucin se encuentra en el fichero EJ19_01.CPP
19.2) La solucin se encuentra en el fichero EJ19_02.CPP
19.4) La solucin se encuentra en el fichero EJ19_04.CPP
19.5) La solucin se encuentra en el fichero EJ19_05.CPP
20. Introduccin a la STL
20.1-2) La solucin se encuentra en el fichero EJ20_01.CPP
20.3) La solucin se encuentra en los ficheros EJ20_03a.CPP, EJ20_03b.CPP y EJ20_03c.CPP.
20.4) La solucin se encuentra en el fichero EJ20_04.CPP
20.5) La solucin se encuentra en el fichero EJ20_05.CPP
21. Contenedores y adaptadores
21.1) La solucin se encuentra en el fichero EJ21_01.CPP
21.2) La solucin se encuentra en el fichero EJ21_02.CPP

21.3) La solucin se encuentra en los ficheros EJ21_03a.CPP y EJ21_03b.CPP.


21.4) La solucin se encuentra en los ficheros EJ21_04a.CPP y EJ21_04b.CPP.
21.5) La solucin se encuentra en el fichero EJ21_05.CPP
21.6) La solucin se encuentra en el fichero EJ21_06.CPP
21.7) La solucin se encuentra en el fichero EJ21_07.CPP
21.8) La solucin se encuentra en el fichero EJ21_08.CPP
21.9) La solucin se encuentra en el fichero EJ21_09.CPP
22. Objetos-funciones y algoritmos
22.1) La solucin se encuentra en el fichero EJ22_01.CPP
22.2) La solucin se encuentra en el fichero EJ22_02.CPP
22.3) La solucin se encuentra en el fichero EJ22_03.CPP
22.4) La solucin se encuentra en el fichero EJ22_04.CPP
22.5) La solucin se encuentra en el fichero EJ22_05.CPP
22.6) La solucin se encuentra en el fichero EJ22_06.CPP
22.7) La solucin se encuentra en el fichero EJ22_07.CPP
23. El Proceso de Desarrollo con C++
23.1) Est en INTERPRETE.CPP

Potrebbero piacerti anche