FAQ Lite
Input/output via <iostream.h> e <stdio.h>

[ 15.1 ] Porque eu devo usar <iostream.h> em lugar do tradicional <stdio.h> ?
[ 15.2 ] Porque meu programa entra em loop infinito quando alguém entra um caracter não-válido ?
[ 15.3 ] Como funciona essa sintaxe while (cin >> foo) ?
[ 15.4 ] Porque meu input parece ainda em processamento após o fim-de-arquivo?
[ 15.5 ] Porque meu programa ignora meu pedido de input após a primeira interação ?
[ 15.6 ] Como eu posso prover impressão para minha class Fred ?
[ 15.7 ] Como eu posso prover input para minha class Fred ?
[ 15.8 ] Como eu posso prover impressão para toda uma hierarquia de classes?
[ 15.9 ] Como eu posso reabrir cin e cout em modo binário, sob DOS e/ou OS/2?
[ 15.10] Porque eu não posso abrir um arquivo em um diretório de diferente, tal como "...\test.dat" ?

[ 15.1 ] Porque eu devo usar <iostream.h> em lugar do tradicional
<stdio.h> ?

Melhor segurança quanto aos tipos dos dados, melhor performance do programa, extensibilidade e facilidades de definição de sub-classes.

printf() é defensável e scanf() é bem utilizável, embora propensa a erros, entretanto ambas são limitadas com respeito ao que os recursos de I/O do C++ podem fazer. O I/O em C++ (usando << e >>) são, relativamente ao I/O em C (usando printf() e scanf()):

  • Mais seguros quanto aos tipos de dados: Com <iostream.h>, o tipo do objeto a ser processado pelo I/O é conhecido estaticamente pelo compilador. Ao contrário, <stdio.h> usa campos "%" para determinar os tipos de dados dinâmicamente.
  • Menos sujeitos a erros: Com <iostream.h> não há indicações redundantes de "%", que devem ser consistentes que os objetos em processamento pelos recursos de I/O. Removendo a redundância remove-se toda uma classe de erros.
  • Mais extensíveis: O mecanismo <iostream.h> do C++ permitem que novos tipos de dados do usuário sejam processados pelos recursos de I/O, sem inutilizar o código existente. Imagine o caos se cada um adicionar, simultaneamente, novos tipos "%" incompatíveis entre si.
  • Aplicáveis ao conceito de sub-classe: O mecanismo <iostream.h> do C++ é construído a partir de classes reais, tais como ostream e istream. Diferentemente de FILE* em <stdio.h> no C, em C++ tem-se classes e conseqüentemente sub-classes. Isso significa que você pode ter outros itens que se parecem e atuam como streams, e ainda fazer com esse streams as coisas mais maravilhosas ou estranhas que você queira. Você automaticamente ganha o uso de zilhões de linhas de código para I/O escritas por desenvolvedores que você sequer conhece, e eles não precisam saber o que você faz em suas classes extended stream
Topo
[ 15.2 ] Porque meu programa entra em loop infinito quando alguém entra um caracter não-válido ?

Por exemplo, suponha que você tenha o seguinte código, que lê inteiros a partir de cin:
    #include <iostream.h>
    
    main()
    {
      cout << "Enter numbers separated by whitespace (use -1 to quit): ";
      int i = 0;
      while (i != -1) {
        cin >> i;        // BAD FORM — See comments below
        cout << "You entered " << i << '\n';
      }
    } 
O problema com esse código é que ele carece de verificações para certificar-se que não está recebendo um caracter não-válido como input. Em particular, se alguém entra com qualquer valor que não se pareça com um inteiro, como por exemplo "x", o stream cin cai em um failed state e todos os inputs subseqüentes são retornados sem qualquer processamento. Em outras palavras, o programa entra em loop infinito. Se 42 foi o último número lido com sucesso, o programa vai exibir a mensagem You entered 42 repetidamente.

Uma maneira simples de verificar os dados de entrada é retirar a requisição de entrada do corpo do loop while, e colocá-la na expressão de controle do loop while. Por exemplo:

    #include <iostream.h>
    
    main()
    {
      cout << "Enter a number, or -1 to quit: ";
      int i = 0;
      while (cin >> i) {    // GOOD FORM
        if (i == -1) break;
        cout << "You entered " << i << '\n';
      }
    } 
Isso fará com que o loop while se encerre quando você receber um fim-de-arquivo, receber um valor não-inteiro e ainda quando você receber uma valor -1.

Naturalmente você pode eliminar o break alterando a expressão de controle do loop while de

while (cin >> i)

para

while ((cin >> i) && (i != -1))

mas esse não é realmente o ponto de interesse central desse FAQ, já que esse FAQ ocupa-se de iostreams, e não de critérios para programação estruturada.

Topo
[ 15.3 ] Como funciona essa sintaxe while (cin >> foo) ?

Veja o item anterior desse FAQ para ter um exemplo de funcionamento de uma sintaxe como while (cin >> foo)

A expressão (cin >> foo) chama o operator>> apropriado (por exemplo, ela chama o operator>> que coloca istream no lado esquerdo da expressão e, se foo for do tipo int, um int& no lado direito). A função istream operator>> retorna seu argumento a esquerda por convenção, o qual, nesse caso, significa que ela vai retornar cin. Em seguida o compilador percebe que o istream retornado está em um contexto booleano, então ele chama o operador cast istream::operator bool(). Ou seja, nesse caso ele chama cin.operator bool (), exatamente como se você tivesse moldado explicitamente (bool) cin ou bool(cin).
(Nota: Se seu compilador ainda não suporta o tipo
bool, ele então chamará istream::operator void*)

O operador molde operator bool() retorna true se o stream estiver em bom estado, ou false se o stream estiver em um failed state (no caso de void*, os valores retornados serão um pointer não-NULL, ou um pointer NULL respectivamente). Por exemplo, se você faz a operação de leitura além da conta, isto é, faz operação de leitura após ter lido o fim-de-arquivo), ou se a informação atual no stream de entrada não é um valor válido para o tipo foo (por exemplo, se foo é um int e o dado é um caracter "x") o stream vai cair em um failed state e o operador molde vai retornar false

A razão pela qual operator>> não retorna simplesmente um valor booleano indicando que a operação foi bem sucedida é para suportar sintaxe cascading

cin >> foo >> bar;

O operator>> é transitivo a esquerda, o que significa que o comando acima é verificado sintaticamente como:

cin >> foo) >> bar;

Em outras palavras, se nós substituirmos operator>> pelo nome de uma função normal como readFrom(), isso se torna a expressão:

readFrom( readFrom(cin, foo), bar);

Como sempre, começamos a resolução pela expressão mais interna. Devido a associatividade a esquerda de operator>>, a expressão cin >> foo se torna a expressão mais a esquerda. Essa expressão retorna cin (mais precisamente, retorna a referência para o seu argumento da esquerda) para a próxima expressão. A próxima expressão também retorna (a referência para) cin, mas essa segunda referência é ignorada, uma vez que é a expressão mais externa nesse conjunto de expressões.

Topo
[ 15.4 ] Porque meu input parece ainda em processamento após o fim-de-arquivo?

Porque o estado fim-de-arquivo (eof) não é determinado até que uma tentativa de leitura seja feita além do fim do arquivo. Ou seja, ler o último byte de um arquivo não determina o estado de fim-de-arquivo.

Por exemplo, o código seguinte tem um erro de off-by-one com o contador i:

    int i = 0;
    while (! cin.eof()) {   // WRONG!
      cin >> x;
      ++i;
      // Work with x ...
    }
Do que você realmente precisa é:
    int i = 0;
    while (cin >> x) {      // RIGHT!
      ++i;
      // Work with x ...
    } 
Topo
[ 15.5 ] Porque meu programa ignora meu pedido de input após a primeira interação ?

Porque o extrator numérico deixa não-dígitos atrás do buffer de entrada.

Se o seu código se parece com:

    char name[1000];
    int age;
    
    for (;;) {
      cout << "Name: ";
      cin >> name;
      cout << "Age: ";
      cin >> age;
    } 
O que você realmente quer é:
    for (;;) {
      cout << "Name: ";
      cin >> name;
      cout << "Age: ";
      cin >> age;
      cin.ignore(INT_MAX, '\n');
    } 
Topo
[ 15.6 ] Como eu posso prover impressão para minha class Fred ?

Use sobrecarga de operator para prover um operador friend de deslocamento para a esquerda, operator <<
    #include <iostream.h>
    
    class Fred {
    public:
      friend ostream& operator<< (ostream& o, const Fred& fred);
      // ...
    private:
      int i_;    // Just for illustration
    };
    
    ostream& operator<< (ostream& o, const Fred& fred)
    {
      return o << fred.i_;
    }
    
    main()
    {
      Fred f;
      cout << "My Fred object: " << f << "\n";
    } 
Nós usamos uma função não-membro (uma friend nesse caso) uma vez que o objeto Fred é um operando à direita do operador <<. Se o objeto Fred pudesse estar do lado esquerdo de <<, ou seja,

myFred << cout

ao invés de

cout << myFred

nós poderíamos ter usado uma função membro nomeada como operator <<

Note que operator << retorna o stream. Essa é a razão pela qual as operações de input podem ser em cascata.

Topo
[ 15.7 ] Como eu posso prover input para minha class Fred ?

Use sobrecarga de operator para prover um operador friend de deslocamento para a direita, operator >>. Isso é similar ao operador de saida, exceto que o parâmetro não tem um const: "Fred&" ao invés de "const Fred&"
    #include <iostream.h>
    
    class Fred {
    public:
      friend istream& operator>> (istream& i, Fred& fred);
      // ...
    private:
      int i_;    // Just for illustration
    };
    
    istream& operator>> (istream& i, Fred& fred)
    {
      return i >> fred.i_;
    }
    
    main()
    {
      Fred f;
      cout << "Enter a Fred object: ";
      cin >> f;
      // ...
    } 
Note que operator >> retorna o stream. É por isso que operações de input podem ser usadas em cascata e/ou usadas em loop while ou comando if.
Topo
[ 15.8 ] Como eu posso prover impressão para toda uma hierarquia de classes?

Providencie um friend operator << que chame uma função  protected virtual.
    class Base {
    public:
      friend ostream& operator<< (ostream& o, const Base& b);
      // ...
    protected:
      virtual void print(ostream& o) const;
    };
    
    inline ostream& operator<< (ostream& o, const Base& b)
    {
      b.print(o);
      return o;
    }
    
    class Derived : public Base {
    protected:
      virtual void print(ostream& o) const;
    }; 
O resultado final é que operator << atua como se fosse resolvido dinamicamente, mesmo tratando-se como uma função friend. Isso é chamado Virtual Friend Function Idiom.

Note que classes derivadas impõem-se sobre print(ostream&) const. Em particular, elas não provêm seus próprio operator <<.

Naturalmente se base é uma ABC, Base::print(ostream&) const pode ser declarada virtual pura usando a sintaxe " usando a sintaxe "= 0"

Topo
[ 15.9 ] Como eu posso "reabrir" cin e cout em modo binário, sob DOS e/ou OS/2?

Essa é uma implementação dependente do compilador. Verifique na documentação de seu compilador.

Por exemplo, suponha que você quer fazer I/O de arquivo binário usando cin e cout. Suponha ainda que seu sistema operacional (seja DOS ou OS/2) insista em traduzir "\r\n" para "\n" em input de cin, e "\n" para "\r\n" em output para cout ou cerr.

Infelizmente não há um modo padrão para fazer cin, cout e/ou cerr serem abertos em modo binário. Fechar os streams e tentar reabri-los em modo binário pode provocar resultados inesperados e indesejáveis.

Em sistemas onde isso faz diferença, a implementação deve prover um meio de torná-los streams binários, mas você vai ter que verificar nos manuais do seu compilador para descobrir como.

Topo
[ 15.10 ] Porque eu não posso abrir um arquivo em um diretório de diferente, tal como "...\test.dat" ?

Porque "\t" é um caracter de tabulação.

Você deve usar barras normais ("/") em seus nomes de arquivos, mesmo que o sistema operacional use barras invertidas, como no DOS, Windows, OS/2 etc. Por exemplo:

    #include <iostream.h>
    #include <fstream.h>
    
    main()
    {
      #if 1
        ifstsream file("../test.dat");     // RIGHT!
      #else
        ifstsream file("..\test.dat");     // WRONG!
      #endif
    
      // ...
    } 
Lembre-se: a barra invertida ("\") é usada em literais para criar caracteres especiais: "\n" é um newline, "\b" é um backspace, e "\t" é um tab, "\a" é um alert, "\v" é um tab vertical, etc. Assim o nome de arquivo

"\version\next\alpha\beta\test.dat"

é interpretado como um grupo de caracteres especiais; use

"/version/next/alpha/beta/test.dat"

mesmo em sistemas que usam "\" como separador de diretórios, como o DOS, Windows, OS/2, etc.

Topo Anterior Próximo Índice
C++ FAQ Lite
Copyright © 1991-98 by Marshall Cline Ph.D., cline@parashift.com
Tradução: Dagoberto Haele Arnaut

| Home | Bookmarks | Universidades | Para Saber mais | Universidades | WEB Directory | Mapa do site |