2010-11-23 9 views
26

je me trouve dérouté à plusieurs reprises par les drapeaux rdstate() - good(), bad(), eof(), fail() - et comment ils sont exprimés en basic_ios::operator!, operator bool et operator void*.Sémantique des drapeaux sur basic_ios

Quelqu'un pourrait-il me sortir de ma misère et expliquer cela afin que je n'aie plus jamais à réfléchir à deux fois?

Répondre

21

Il y a trois drapeaux qui indiquent l'état d'erreur:

  • badbit signifie quelque chose a très mal avec le flux. Il peut s'agir d'une erreur de tampon ou d'une erreur dans tout ce qui alimente les données du flux. Si ce drapeau est défini, il est probable que vous n'allez plus utiliser le flux.

  • failbit signifie qu'une extraction ou une lecture du flux a échoué (ou une écriture ou une insertion pour les flux de sortie) et que vous devez être conscient de cette défaillance.

  • eofbit signifie que le flux d'entrée a atteint sa fin et qu'il n'y a plus rien à lire. Notez que ce paramètre est défini uniquement lorsque vous essayez de lire un flux d'entrée ayant atteint sa fin (c'est-à-dire qu'il est défini lorsqu'une erreur se produit parce que vous essayez de lire des données qui n'y figurent pas).

Le failbit peut également être défini par de nombreuses opérations qui atteignent EOF. Par exemple, s'il ne reste plus que des espaces dans le flux et que vous essayez de lire un int, vous atteindrez EOF et vous ne pourrez pas lire le int, les deux indicateurs seront donc définis.

La fonction fail() teste badbit || failbit. Les tests de fonctionnement good()!(badbit || failbit || eofbit) C'est-à-dire qu'un flux est bon quand aucun des bits n'est défini.

Vous pouvez réinitialiser les indicateurs en utilisant la fonction membre ios::clear(); cela vous permet de définir l'un des drapeaux d'erreur; par défaut (sans argument), il efface les trois drapeaux.

Les flux ne sont pas surchargés operator bool(); operator void*() est utilisé pour implémenter une version quelque peu cassée de l'idiome de sécurité bool. Cette surcharge d'opérateur renvoie null si badbit ou failbit est défini et non-null sinon. Vous pouvez l'utiliser pour soutenir l'idiome de tester le succès d'une extraction comme la condition d'une boucle ou un autre état des flux de contrôle:

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

La surcharge operator!() est à l'opposé de la operator void*(); il renvoie true si badbit ou failbit est défini et false sinon. La surcharge operator!() n'est plus vraiment nécessaire; il remonte à avant les surcharges de l'opérateur ont été pris en charge complètement et de manière cohérente (voir la question de sbi "Why does std::basic_ios overload the unary logical negation operator?"). C++ 0x corrige le problème qui nous oblige à utiliser l'idiome booléen sécurisé. Ainsi, en C++ 0x, le modèle de classe de base basic_ios surcharge operator bool() en tant qu'opérateur de conversion explicite; cet opérateur a la même sémantique que l'actuel operator void*().

+1

Bon est mal nommé (et donc mal appliqué), car tester good() ne vous dit pas si la dernière opération a réussi ou non. En d'autres termes, stream.good() n'est pas équivalent à bool (stream). –

16

En plus de James' answer, il est important de se rappeler que ces drapeaux indiquent résultats des opérations, donc ne seront pas réglées, sauf si vous effectuez l'une.

Une erreur commune est de faire ceci:

#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 
    } 
} 

Le problème ici est que eof() ne sera pas réglé avant après nous essayons d'obtenir la dernière ligne, à quel point le flux dira " non, pas plus! et définissez-le. Cela signifie que la "bonne" façon est la suivante:

#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; 
    } 
} 

Ceci place la vérification à l'emplacement correct. C'est très indiscipliné cependant; heureusement pour nous, la valeur de retour pour std::getline est le flux, et le flux a un opérateur de conversion qui lui permet d'être testé dans un contexte booléen, avec la valeur de fail(), qui comprend eof(). Nous pouvons donc écrire:

#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 parce qu'il est important de le souligner. Les E/S sont effectuées à tort trop souvent en C++. –

+0

Merci beaucoup, cette question a été soulevée sur un q hier aussi et (surprise!) M'a déconcerté au départ, même si cela a du sens une fois que vous y pensez. –