Основная идея буферизации - объединить операции в более крупные куски: вместо чтения небольшого количества байтов прочитайте целую страницу и сделайте ее доступной по запросу; вместо того, чтобы писать небольшое количество байтов, буферизировать их и написать целую страницу или когда запись явно запрашивается. По сути, это важная оптимизация производительности. Это довольно четкое сокращение операций ввода-вывода, но в целом применяется и для других целей: обработка нескольких блоков одновременно обычно заканчивается быстрее, чем обработка отдельных блоков.
Что касается ввода/вывода , то промывка относится к письму буферизированных байтов в настоящее время - что бы это ни значило на практике. Для C++ IOStreams поток потока сводится к вызову функции-члена std::ostream::flush()
, которая, в свою очередь, вызывает std::streambuf::pubsync()
в связанном потоковом буфере (это игнорирует детали, которые потоком фактически являются шаблонами классов, например.std::basic_ostream<cT, traits>
; для целей этого обсуждения не имеет значения, что они являются шаблонами классов): базовый класс std::streambuf
- это абстракция C++ о том, как должен обрабатываться определенный поток. Он концептуально состоит из входного и выходного буфера плюс виртуальная функция, отвечающая за чтение или запись буферов, соответственно. Функция std::streambuf::pubsync()
вызывает виртуальную функцию std::streambuf::sync()
, которая должна быть переопределена для каждого буфера потока, который потенциально буферизует символы. То есть, то, что flushing фактически означает, зависит от реализации этой виртуальной функции.
Является ли переопределение sync()
действительно чем-то, и то, что он делает, явно зависит от того, что представляет собой буфер потока. Например, для std::filebuf
, который отвечает за чтение или запись в файл, sync()
записывает текущее содержимое буфера и удаляет сбрасываемые символы из буфера. Учитывая, что файл не может действительно представлять физический файл, но, например, файл. именованный канал для связи с другим процессом, это разумное поведение. С другой стороны, промывка std::stringbuf
, которая является буфером потока, используемым для реализации записи на используемом std::string
, например. по std::ostringstream
фактически ничего не делает: строка целиком находится в пределах программы, а std::string
, представляющая ее значение, создается при вызове функции-члена std::stringbuf::str()
. Для пользовательских потоковых буферов существует много разных способов поведения. Обычный класс потоковых буферов фильтрует выход каким-то образом перед его передачей в другой буфер потока (например, буфер регистрации может добавлять отметку времени после каждой новой строки). Обычно они просто вызывают функцию std::streambuf::pubsync()
следующих буферов потока.
Таким образом, описания фактического поведения обычно остаются довольно неопределенными, потому что не совсем ясно, что именно происходит. Понятно, что сброс потока или вызов pubsync()
в буфере потока должен обновлять внешний адрес символа в соответствии с текущим внутренним состоянием. Как правило, это означает пересылку буферизованных символов в текущий момент и удаление их из внутреннего буфера. На этом этапе стоит отметить, что буфер обычно также записывается, когда он просто заполнен. Когда он заполняется, зависит, опять же, от конкретного буфера потока. Хорошая реализация std::filebuf
по существу заполняет буфер байтов, соответствующий размеру базовой страницы (или ее нескольких), а затем записывает полные страницы, сводя к минимуму количество операций ввода-вывода (это на самом деле относительно сложно сделать, потому что буфер размеры различны между различными файловыми системами и в зависимости от кодировки, используемой при записи количества созданных байтов, не могут быть легко оценены).
Стандарта C++ библиотеки обычно не требует явного притоки:
- Поток
std::cerr
устанавливается автоматически промывать любой выход, полученный после каждого вывод операций при вызове. Это результат того, что флаг форматирования std::ios_base::unitbuf
установлен по умолчанию. Чтобы отключить эту функцию, вы можете использовать std::cerr << std::nounitbuf
или можете использовать только std::clog
, который пишет в тот же пункт назначения, но не выполняет эту очистку.
- При считывании с
std::istream
«связанный» std::ostream
, если есть, сбрасывается. По умолчанию std::cout
привязан к std::cin
. Если вы хотите настроить связанный std::ostream
самостоятельно, вы можете использовать, например. in.tie(&out)
или, если вы хотите удалить связанный std::ostream
, вы можете использовать, например. std::cin.tie(0)
.
- Когда
std::ostream
уничтожен (или если он обычно будет уничтожен в случае, если std::ostream
является одним из стандартных потоков), то std::ostream
размывается.
- Когда буфера потока будет переполняться виртуальная функция, вызывается
std::streambuf::overflow()
, которая обычно записывает буфер текущего буфера (плюс переданный символ, если есть) в пункт назначения. Это часто делается путем вызова sync()
для очистки текущего буфера, но то, что делается точно, опять же зависит от конкретного потока потока.
Вы также можете явно запросить промывке std::ostream
:
- Вы можете вызвать функцию-член
std::ostream::flush()
, например, std::cout.flush()
.
- Вы можете вызвать функцию-член
std::streambuf::pubsync()
, например. std::cout.rdbuf()->pubsync()
(при условии, что установлен буфер потока, вызов std::ostream::flush()
ничего не сделает, если нет буфера потока).
- Вы можете использовать манипулятор
std::flush
, например. std::cout << std::flush
.
- Вы можете использовать манипулятор
std::endl
, например. std::cout << std::endl
, чтобы сначала написать символ новой строки, за которым следует промывка потока. Обратите внимание, что вы должны использовать std::endl
только, когда вы действительно хотите сбросить выходной сигнал. Не используйтеstd::endl
, когда вы на самом деле просто хотите создать конец строки! Просто напишите символ новой строки для последнего!
В любом случае, если вы просто напишете пару символов, которые не вызывают буфера буфера потока, с ними ничего не произойдет, пока они не будут либо неявно, либо явно сброшены. Как правило, это не проблема, поскольку нормальный случай, когда требуется получить буферизованный выход, то есть при чтении с std::cin
, обрабатывается std::cout
, являющимся tie()
d, до std::cin
. Когда используются другие механизмы ввода/вывода, может потребоваться явно указать потоки, связанные с потоком. Буфер иногда является проблемой, если программа «сбой» или утверждает во время отладки, поскольку некоторые выходы в поток, возможно, были сделаны, но еще не отправлены. Средством для этого является использование std::unitbuf
.
Этот вопрос был задан и ответил много времени в SO. http://stackoverflow.com/a/4752069/1155650 –