Como se ilustraba en el extractor de
Date
, debe estar alerta por las entradas erróneas.
Si la entrada produce un valor inesperado, el proceso se tuerce y es
difícil de recuperar. Además, por defecto, la entrada formateada está
delimitada por espacios en blanco. Considere que ocurre cuando recogemos
los fragmentos de código anteriores en un solo programa:
//: V2C04:Iosexamp.cpp {RunByHand}
y le proporcionamos la siguiente entrada:
12 1.4 c this is a test
esperamos la misma salida que si le hubieramos proporcionado esto:
12 1.4 c this is a test
pero la salida es algo inesperado
i = 12 f = 1.4 c = c buf = this 0xc
Nótese que buf
solo tiene la primera
palabra porque la rutina de entrada busca un espacio que delimite la
entrada, que es el que se encuentra después de 'tihs.' Además, si la
entrada continua de datos es mayor que el espacio reservado por
buf
, sobrepasamos los limites del buffer.
En la práctica, usualmente deseará obtener la entrada desde programas interactivos, una linea cada vez como secuencia de carácteres, leerla, y después hacer las conversiones necesarias hasta que estén seguras en un buffer. De esta manera no deberá preocuparse por la rutina de entrada fallando por datos inesperados.
Otra consideración es todo el concepto de interfaz de línea de
comandos. Esto tenia sentido en el pasado cuando la consola era la única
interfaz con la máquina, pero el mundo está cambiando rápidamente hacia
otro donde la interfaz gráfica de usuario (GUI) domina. ¿Cual es el
sentido de la E/S por consola en este mundo? Esto le da mucho más sentido
a ignorar cin
en general, salvo para ejemplos
simples y tests, y hacer los siguientes acercamientos:
Si su programa requiere entrada, ¿leer esta entrada desde
un fichero? Pronto verá que es remarcablemente fácil usar ficheros con
iostream
. Iostream
para
ficheros todavia funciona perfectamente con una GUI.
Leer la entrada sin intentar convertirla, como hemos sugerido. Cuando la entrada es algun sitio donde no podemos arriesgarnos durante la conversión, podemos escanearla de manera segura.
La salida es diferente. Si está usando una interfaz
gráfica, cout
no necesariamente funciona, y usted
debe mandarlo a un fichero (que es indéntico a mandarlo a un
cout
) o usar los componentes del GUI para mostrar
los datos. En cualquier otra situacion, a menudo tiene sentido mandarlo a
cout
. En ambos casos, la funciones de formateo de
la salida de iostream
son muy útiles.
Otra práctica común ahorra tiempo en compilaciones largas. Consideres, por ejemplo, cómo quiere declarar los operadores del stream Date introducidos antes en el capítulo en un fichero de cabecera. Usted solo necesita incluir los prototipos para las funciones, luego no es necesario incluir la cabecera entera de <iostream> en Date.h. La práctica estándar es declarar solo las clases, algo como esto:
class ostream;
Esta es una vieja tecnica para separar la interfaz de la implementación y
a menudo la llaman declaración avanzada( y ostream
en este punto debe ser
considerada un tipo incompleto, ya que la definición de la clase no ha sido vista
todavia por el compilador).
Esto con funcionará asi, igualmente, por dos razones:
Las clases stream estan definidas en el espacio de nombres std
.
Son plantillas.
La declaración correcta debería ser:
namespace std { template<class charT, class traits = char_traits<charT> > class basic_ostream; typedef basic_ostream<char> ostream; }
(Como puede ver, como las clase string, las clases stream usan las clases de rasgos de caracter mencionadas en el Capítulo 3). Como puede ser terriblemente tedioso darle un tipo a todas las clases stream a las que quiere referenciar, el estándar provee una cabecera que lo hace por usted:
// Date.h #include <iosfwd> class Date { friend std::ostream& operator<<(std::ostream&, const Date&); friend std::istream& operator>>(std::istream&, Date&); // Etc.