2010-11-23 15 views
26

me encuentro en repetidas ocasiones desconcertados por las banderas rdstate() - good(), bad(), eof(), fail() - y la forma en que se expresan en basic_ios::operator!, operator bool y operator void*.Semántica de banderas en basic_ios

¿Alguien podría sacarme de mi miseria y explicarme esto para no tener que pensar dos veces?

Respuesta

21

Hay tres indicadores que indican el estado de error:

  • badbit significa que algo ha ido muy mal con la corriente. Puede ser un error de búfer o un error en lo que sea que esté alimentando datos a la transmisión. Si esta bandera está configurada, es probable que ya no vaya a utilizar la transmisión.

  • failbit significa que ha fallado una extracción o una lectura de la secuencia (o una escritura o inserción de flujos de salida) y debe tener en cuenta dicha falla.

  • eofbit significa que el flujo de entrada ha llegado a su fin y no queda nada por leer. Tenga en cuenta que esto se establece solo después de intentar leer de una secuencia de entrada que ha llegado a su final (es decir, se establece cuando se produce un error porque intenta leer datos que no están allí).

El failbit también puede configurarse en muchas operaciones que alcanzan EOF. Por ejemplo, si solo queda un espacio en blanco en la transmisión e intenta leer un int, ambos llegarán a EOF y no podrán leer el int, por lo que se establecerán ambos indicadores.

La función fail() prueba badbit || failbit.

La función good() prueba !(badbit || failbit || eofbit). Es decir, una transmisión es buena cuando no se establece ninguno de los bits.

Puede restablecer los indicadores mediante la función de miembro ios::clear(); esto le permite establecer cualquiera de los indicadores de error; por defecto (sin argumento), borra los tres indicadores.

Las corrientes no sobrecargan operator bool(); operator void*() se usa para implementar una versión algo rota de la expresión idiomática segura bool. Esta sobrecarga del operador devuelve un valor nulo si badbit o failbit están configurados, y no son nulos en caso contrario. Usted puede usar esto para apoyar el idioma de la prueba del éxito de una extracción como la condición de una declaración de bucle o de otro flujo de control:

if (std::cin >> x) { 
    // extraction succeeded 
} 
else { 
    // extraction failed 
} 

La sobrecarga operator!() es lo contrario de la operator void*(); devuelve true si se establece badbit o failbit y false en caso contrario. La sobrecarga operator!() ya no es necesaria; se remonta a antes de que las sobrecargas del operador fueran compatibles por completo y de manera consistente (vea la pregunta de sbi "Why does std::basic_ios overload the unary logical negation operator?").

C++ 0x soluciona el problema que causa que tengamos que usar la expresión segura bool, por lo que en C++ 0x la plantilla de clase base basic_ios sobrecarga operator bool() como un operador de conversión explícito; este operador tiene la misma semántica que el actual operator void*().

+1

Bueno es mal llamado (y por lo tanto mal aplicado), porque la prueba de bueno() no le dice si la última operación tuvo éxito o no. En otras palabras, stream.good() no es equivalente a bool (stream). –

16

Además de James' answer, es importante recordar que estos indicadores indican resultados de operaciones, por lo que no se establecerán a menos que realice uno.

Un error común es hacer esto:

#include <fstream> 
#include <iostream> 
#include <string> 

int main() 
{ 
    std::ifstream file("main.cpp"); 

    while (!file.eof()) // while the file isn't at eof... 
    { 
     std::string line; 
     std::getline(file, line); // ...read a line... 

     std::cout << "> " << line << std::endl; // and print it 
    } 
} 

El problema aquí es que eof() no se establecerá hasta después tratamos de obtener la última línea, momento en el que la corriente va a decir " ¡No, no más! " y establecerlo. Esto significa que la forma "correcta" es:

#include <fstream> 
#include <iostream> 
#include <string> 

int main() 
{ 
    std::ifstream file("main.cpp"); 

    for (;;) 
    { 
     std::string line; 
     std::getline(file, line); // read a line... 

     if (file.eof()) // ...and check if it we were at eof 
      break; 

     std::cout << "> " << line << std::endl; 
    } 
} 

Esto coloca el cheque en la ubicación correcta. Esto es muy rebelde sin embargo; afortunadamente para nosotros, el valor de retorno para std::getline es la secuencia, y la secuencia tiene un operador de conversión que permite que se pruebe en un contexto booleano, con el valor de fail(), que incluye eof(). Entonces podemos simplemente escribir:

#include <fstream> 
#include <iostream> 
#include <string> 

int main() 
{ 
    std::ifstream file("main.cpp"); 

    std::string line; 
    while (std::getline(file, line)) // get line, test if it was eof 
     std::cout << "> " << line << std::endl; 
} 
+3

+1 porque es importante resaltar esto. La E/S se hace erróneamente con demasiada frecuencia en C++. –

+0

Muchas gracias, este problema también apareció ayer y (¡sorpresa!) Me desconcertó inicialmente, aunque tiene sentido una vez que lo piensas. –