Ответ 1
Так как на него не ответил, что std::flush
, вот некоторые подробности о том, что это на самом деле. std::flush
является манипулятором, то есть функцией со специальной сигнатурой. Чтобы начать с простого, вы можете думать о std::flush
наличия подписи
std::ostream& std::flush(std::ostream&);
Реальность немного сложнее, хотя (если вам интересно, это объясняется ниже).
Операторы перегрузки класса потока перехватывают операторы этой формы, т.е. существует функция-член, принимающая манипулятор в качестве аргумента. Оператор вывода вызывает манипулятор с самим объектом:
std::ostream& std::ostream::operator<< (std::ostream& (*manip)(std::ostream&)) {
(*manip)(*this);
return *this;
}
То есть, когда вы "выводите" std::flush
с помощью std::ostream
, он просто вызывает соответствующую функцию, т.е. следующие два утверждения эквивалентны:
std::cout << std::flush;
std::flush(std::cout);
Теперь std::flush()
сам по себе довольно прост: все, что он делает, это вызвать std::ostream::flush()
, то есть вы можете представить себе его реализацию, чтобы выглядеть примерно так:
std::ostream& std::flush(std::ostream& out) {
out.flush();
return out;
}
Функция std::ostream::flush()
технически вызывает std::streambuf::pubsync()
в буфере потока (если есть), который связан с потоком: буфер потока отвечает за буферизацию символов и отправку символов во внешний адрес, когда использованный буфер переполняется или когда внутреннее представление должно синхронизироваться с внешним получателем, т.е. когда данные должны быть сброшены. Синхронизация последовательного потока с внешним адресатом означает, что любые буферизованные символы немедленно отправляются. То есть использование std::flush
заставляет буфер потока очищать свой выходной буфер. Например, когда данные записываются в консольную промывку, символы появляются в этой точке на консоли.
Это может поставить вопрос: почему персонажи сразу не написаны? Простой ответ заключается в том, что писать символы, как правило, довольно медленно. Однако время, необходимое для написания разумного количества символов, по существу идентично написанию только одного. Количество символов зависит от многих характеристик операционной системы, файловых систем и т.д., Но часто до чего-то вроде 4k символов написано примерно в то же время, что и один символ. Таким образом, буферизация символов перед отправкой их с использованием буфера в зависимости от деталей внешнего адресата может быть огромным улучшением производительности.
Вышеупомянутое должно ответить на два из ваших трех вопросов. Остается вопрос: когда вы промоете поток? Ответ: Когда символы должны быть записаны во внешнее место назначения! Это может быть в конце написания файла (закрытие файла неявно очищает буфер) или непосредственно перед запросом ввода пользователя (обратите внимание, что std::cout
автоматически сбрасывается при чтении из std::cin
, поскольку std::cout
есть std::istream::tie()
'd до std::cin
). Хотя может быть несколько случаев, когда вы явно хотите очистить поток, я считаю их довольно редкими.
Наконец, я обещал дать полное представление о том, что на самом деле есть std::flush
: потоки - это шаблоны классов, способные работать с разными типами символов (на практике они работают с char
и wchar_t
; другие персонажи довольно вовлечены, хотя это возможно, если вы действительно настроены). Чтобы иметь возможность использовать std::flush
со всеми экземплярами потоков, это, как правило, шаблон функции с такой сигнатурой:
template <typename cT, typename Traits>
std::basic_ostream<cT, Traits>& std::flush(std::basic_ostream<cT, Traits>&);
При непосредственном использовании std::flush
с экземпляром std::basic_ostream
это не имеет значения: компилятор автоматически выводит аргументы шаблона. Однако в тех случаях, когда эта функция не упоминается вместе с чем-то, облегчающим вывод аргумента шаблона, компилятор не сможет вывести аргументы шаблона.