quinta-feira, 3 de fevereiro de 2011

'\n' ou std::endl - Qual newline usar? (C++)

Seja por economia de espaço ou por pura preguiçite aguda é comum que uma boa parte dos programadores escolha usar '\n' para quebrar um linha, principalmente para logar informações de debug no console. No entanto, qual é a diferença? Bem a diferença principal é que o std::endl faz um flush no buffer (para streams que suportam buffer) então se você não quer que o buffer receba um flush com muita frequencia é melhor usar o \n, mas se você quer evitar o flush automatico do buffer e deseja ter um controle melhor de quando o buffer será comitado use std::endl.

De um ponto de vista simplista:

std::cout << std::endl;

// é equivalente a

std::cout << "\n" << std::flush;

Claro que quem estiver usando o operador '<<' deve saber que operações de I/O, mesmo bufferizadas, são operações lentas. Então não leve o assunto performace TÃO a sério nesse cenário. Em contra partida lembre que o flush pode criar algumas chamadas a mais e se o seu código é uma classe de log em arquivo no disco, por exemplo, então você pode ter uma perda de performace SIM. Vale lembrar que isso acontece pois, no geral, escrever um bloco de bytes é mais economico do que escrever varios bytes um de cada vez, os dispositivos são desenhados para efetuar operações com multiplos bytes então aproveite essa feature! E lembre-se que no geral, para arquivos abertos em modo texto, o newline será mapeado para o caracter correto de fim de linha(LF em Unix, CRLF no DOS(Windows), CR no Macintosh, e por ai vai), vale lembrar que cout e cin são arquivos especiais, ou uma especie de arquivo(se você prefere assim) e também vão se comportar como tal. Para não manter o artigo tão superficial vamos olhar a implementação do endl na libstdc++ da GNU:

/**
*  @brief  Write a newline and flush the stream.
*
*  This manipulator is often mistakenly used when a simple newline is
*  desired, leading to poor buffering performance.  See
*  http://gcc.gnu.org/onlinedocs/libstdc++/27_io/howto.html#2 for more
*  on this subject.
*/

template
basic_ostream<_CharT, _Traits>& endl(basic_ostream<_CharT, _Traits>& __os)
    {
    return flush(__os.put(__os.widen('\n')));
    }

Veja que a documentação é bem clara quanto ao comportamento do endl, mas olhando o código fica claro o que acontece. O endl recebe o stream que vai operar como parâmetro e chama o put para inserir um '\n'. O retorno do put será enviado para o flush causando, obviamente, um flush na stream.

That's all!!!