Сначала немного информации из мира С.
При запуске приложения (речь идёт о POSIX) вместе с ним открывается 3 файловых дескритоптора:
- 0 - ассоциирован со стандартным вводом (stdin)
- 1 - ассоциирован со стандартным выводом (stdout)
- 2 - ассоциирован со стандартным выводом в поток ошибок (stderr)
В стандартной библиотеки Си используется FILE*
-based буфферизируемый доступ к файлам и терминалу. В stdio.h объявлены следующие глобальные символы, которые отражают стандартные потоки ввода вывода:
stdin
stdout
stderr
Соответственно их свободно можно использовать вместе с семейством функций fread()
/fprintf()
, обеспечивая буфферизированный доступ.
Сами файловые дескрипторы POSIX объявляет через макросы в unistd.h:
STDIN_FILENO
STDOUT_FILENO
STDERR_FILENO
Соответственно их свободно можно использовать со всем семейством функций read()
, write()
и, даже, select()
/epoll()
и fcntl()
(например, при помощи fcntl(O_NONBLOCK)
+select
+read
можно реализовать аналог [getch()](http://www.codenet.ru/progr/cpp/spr/175.php)
из старого доброго Borland C++). Доступ через эти функции не буфферизирован.
Доступ к одному потоку разными механизмами в одной программе лучше не осуществлять: буферизация это уровень библиотеки C и внутреннего устройства FILE. write()
или read()
ближе к системным вызовам и ничего про это знать не обязаны. Как результат можете получить перемешивание текста даже в однопоточном приложении. Это не отменяет того файла, что сам fwrite()
может, в конце концов, вызвать write()
.
На этом экскурс закончим и вернёмся в C++.
В C++ для работы с потоками служит библиотека [iostream](http://www.cplusplus.com/reference/istream/iostream/)
. Причём доступ к конкретному стриму может быть как буфферизируемым так и не буфферизируемым (зависит от потребностей).
Библиотека декларирует объекты, связанные со стандартными потоками:
std::cin
- стандартный вводstd::cout
- стандартный выводstd::cerr
- стандартный вывод ошибок иstd::clog
- для логирования
Про std::cin
особо гооврить (пока?) не будем - он один, в своём роде. А вот про оставшиеся три стоит.
Итак, начнём с std::cout
и std::cerr
. В C++ они, помимо того, что связаны с разными дескрипторами, несколько отличаются поведением:
std::cout
- буферизируемыйstd::cerr
- не буферизируемый
Такое отличие явственно следует из семантики использования: ошибку нужно увидеть сразу без ожидания каких-то дополнительных действий со стороны программиста типа std::flush
или std::endl
(он, кстати, делает и flush, поэтому, для большей производительности строки в нормальном выводе стоит заканчивать ‘
n’, а не std::endl
).
Ок, а что за std::clog
? А это всего-лишь std::cerr
+ буферизация. И снова, семантика использования проста: диагностика может чуть и подождать, что бы не понижать производительность вывода, но смешиваться с нормальным выводом не есть хорошо, мы, ведь, можем использовать приложения в пайпе и, желательно, разделить диагностику и ошибки от обычного вывода.
Собственно, немного обобщая:
std::cout
- используется для обычного вывода результатов работы программы на экран, эти данные могут быть переданы дальше по пайпу для обработки.std::cerr
- вывод сообщений об ошибке в обработке, что бы не подмешивались в основной поток и не ломали логику работы других программ в пайпе. При этом сообщение выводим максимально скоро.std::clog
- используем для разного рода диагностических сообщений, но когда использованиеstd::cerr
замедляет вывод из-за более частого дёргания системных вызовов, при этом, не подмешиваемся в основной поток и не мешаем работе других приложений в пайплайне.
Хорошим тоном, так же, является вывод справки по программе в std::cout
, если вызвано с параметрами -h|--help
- тогда её удобно смотреть в, например, less без дополнительных телодвижений, а вот справку по опциям, в случае неправильной установки какого-то параметра (или пропуска обязательного), лучше выводить (тут особой разницы не вижу) в std::cerr
или std::clog
.
И, на последок, лекция по поводу тормозов iostream и вообще, а как оно там внутри устроено:
- https://events.yandex.ru/lib/talks/3309/ - просмотр строго рекомендован.