Основная идея буферизации состоит в том, чтобы объединить операции в большие куски: вместо чтения небольшого количества байтов, прочитайте всю страницу и сделайте ее доступной по запросу;вместо того, чтобы записывать небольшое количество байтов, буферизируйте их и записывайте целую страницу или когда явно запрашивается запись.По сути, это важная оптимизация производительности.Он довольно четок для операций ввода-вывода, но в целом применяется и для других целей: обработка нескольких блоков одновременно обычно оказывается быстрее, чем обработка отдельных блоков.
В отношении очистки ввода-вывода относится к записи буферизованных байтов в место назначения - что бы это ни значило на практике.Для C ++ IOStreams очистка потока равносильна вызову функции-члена std::ostream::flush()
, которая, в свою очередь, вызывает std::streambuf::pubsync()
в связанном буфере потока (это игнорирует детали того, что поток на самом деле является шаблоном класса, например, std::basic_ostream<cT, traits>
; для этой целидля обсуждения не имеет значения, являются ли они шаблонами классов): базовый класс std::streambuf
- это абстракция C ++ от способа обработки определенного потока.Концептуально он состоит из входного и выходного буфера плюс виртуальная функция, отвечающая за чтение или запись буферов, соответственно.Функция std::streambuf::pubsync()
вызывает виртуальную функцию std::streambuf::sync()
, которая должна быть переопределена для каждого буфера потока, который потенциально буферизует символы.То есть то, что на самом деле означает сброс , зависит от того, как реализована эта виртуальная функция.
Действительно ли переопределение 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
only , когда вы действительно хотите очистить вывод. Не используйте std::endl
, когда вы на самом деле просто хотите создать конец строки!Просто напишите символ новой строки для последнего!
В любом случае, если вы просто напишите пару символов, которые не вызывают переполнение буфера потокового буфера, с ними ничего не произойдет, пока они не будутлибо явно, либо явно сброшены.Как правило, это не проблема, потому что обычный случай, когда буферизованный вывод должен стать доступным, то есть при чтении из std::cin
, обрабатывается с std::cout
, равным tie()
d до std::cin
.Когда используются другие механизмы ввода / вывода, может потребоваться явно tie()
связанных потоков.Буфер иногда является проблемой, если программа «вылетает» или утверждает во время отладки, потому что некоторые выходные данные в поток, возможно, были сделаны, но еще не были отправлены.Средство для этого - использовать std::unitbuf
.